Règle de Trois devient Règle de Cinq avec C ++ 11?

Donc, après avoir regardé cette magnifique conférence sur les références rvalue, je pensais que chaque classe bénéficierait d’un tel “constructeur de mouvements”, template MyClass(T&& other) edit et bien sûr d’un “opérateur de déplacement”, template MyClass& operator=(T&& other) comme Philipp le fait remarquer dans sa réponse, si des membres sont alloués dynamicment ou stockent généralement des pointeurs. Tout comme vous devriez avoir un copy-ctor, un opérateur d’affectation et un destructeur si les points mentionnés précédemment s’appliquent. Pensées?

    Je dirais que la règle des trois devient la règle des trois, quatre et cinq:

    Chaque classe doit définir explicitement l’un des ensembles de fonctions membres suivants:

    • Aucun
    • Destructeur, constructeur de copie, opérateur d’assignation de copie

    De plus, chaque classe qui définit explicitement un destructeur peut définir explicitement un constructeur de déplacement et / ou un opérateur d’affectation de déplacement.

    Généralement, l’un des ensembles de fonctions membres suivants est sensible:

    • Aucun (pour de nombreuses classes simples où les fonctions de membre spéciales générées implicitement sont correctes et rapides)
    • Destructeur, constructeur de copie, opérateur d’assignation de copie (dans ce cas, la classe ne sera pas mobile)
    • Destructeur, déplacer le constructeur, déplacer l’opérateur d’affectation (dans ce cas, la classe ne sera pas copiable, utile pour les classes de gestion de ressources où la ressource sous-jacente n’est pas copiable)
    • Destructeur, constructeur de copie, opérateur d’assignation de copie, déplacement du constructeur (en raison de l’élision de la copie, il n’y a pas de surcharge si l’opérateur d’atsortingbution de copie prend son argument par valeur)
    • Destructeur, constructeur de copie, opérateur d’assignation de copie, déplacement du constructeur, déplacement de l’opérateur d’affectation

    Notez que le déplacement du constructeur et le déplacement de l’opérateur d’affectation ne seront pas générés pour une classe déclarant explicitement l’une des autres fonctions membres, cet opérateur de copie et d’affectation de copie ne sera pas généré pour une classe déclarant explicitement un constructeur de déplacement ou de déplacement. opérateur d’affectation, et qu’une classe avec un destructeur explicitement déclaré et un constructeur de copie implicitement défini ou un opérateur d’affectation de copie implicitement défini est considérée comme obsolète. En particulier, la classe de base polymorphe C ++ 03 suivante est parfaitement valide

     class C { virtual ~C() { } // allow subtype polymorphism }; 

    doit être réécrit comme suit:

     class C { C(const C&) = default; // Copy constructor C(C&&) = default; // Move constructor C& operator=(const C&) & = default; // Copy assignment operator C& operator=(C&&) & = default; // Move assignment operator virtual ~C() { } // Destructor }; 

    Un peu gênant, mais probablement meilleur que l’alternative (génération automatique de toutes les fonctions membres spéciales).

    Contrairement à la règle des trois grands, où ne pas adhérer à la règle peut causer de sérieux dommages, ne pas déclarer explicitement le constructeur de mouvement et l’opérateur d’affectation de déplacement est généralement correct mais souvent sous-optimal en termes d’efficacité. Comme mentionné ci-dessus, les opérateurs de déplacement de constructeur et d’affectation de déplacement ne sont générés que s’il n’y a pas de constructeur de copie, d’opérateur d’atsortingbution de copie ou de destructeur explicitement déclaré. Ceci n’est pas symésortingque au comportement traditionnel C ++ 03 en ce qui concerne la génération automatique du constructeur de copie et de l’opérateur d’atsortingbution de copie, mais il est beaucoup plus sûr. Ainsi, la possibilité de définir des constructeurs de déplacement et des opérateurs d’affectation de déplacement est très utile et crée de nouvelles possibilités (classes purement mobiles), mais les classes qui adhèrent à la règle C ++ 03 du Big Three seront toujours correctes.

    Pour les classes de gestion de ressources, vous pouvez définir le constructeur de copie et l’opérateur d’assignation de copie comme étant supprimés (ce qui constitue une définition) si la ressource sous-jacente ne peut pas être copiée. Souvent, vous voulez toujours déplacer le constructeur et déplacer l’opérateur d’affectation. Les opérateurs d’affectation de copie et de déplacement seront souvent implémentés en utilisant swap , comme dans C ++ 03. Si vous avez un constructeur de déplacement et un opérateur d’affectation de déplacement, la spécialisation std::swap deviendra sans importance car le fichier std::swap générique utilise le constructeur de déplacement et l’opérateur d’affectation de déplacement si disponible, et cela devrait être assez rapide.

    Les classes qui ne sont pas destinées à la gestion des ressources (c.-à-d. Aucun destructeur non vide) ou le polymorphism de sous-type (c.-à-d. Aucun destructeur virtuel) ne doivent déclarer aucune des cinq fonctions membres spéciales; ils seront tous générés automatiquement et se comporteront correctement et rapidement.

    Je ne peux pas croire que personne ne soit lié à ça .

    Fondamentalement, l’article plaide pour “Rule of Zero”. Il ne convient pas que je cite un article entier, mais je pense que c’est le point principal:

    Les classes qui ont des destructeurs personnalisés, des constructeurs copier / déplacer ou des opérateurs d’affectation de copie / déplacement doivent gérer exclusivement la propriété. Les autres classes ne doivent pas avoir de destructeur personnalisé, de constructeur de copie / déplacement ou d’opérateurs d’affectation de copie / déplacement.

    Aussi ce bit est important à mon humble avis:

    Les classes communes “ownership-in-a-package” sont incluses dans la bibliothèque standard: std::unique_ptr et std::shared_ptr . Grâce à l’utilisation d’objects deleter personnalisés, les deux ont été rendus suffisamment flexibles pour gérer pratiquement tous les types de ressources.

    Je ne le pense pas, la règle de trois est une règle de base qui stipule qu’une classe qui implémente l’un des suivants, mais pas tous, est probablement boguée.

    1. Copier constructeur
    2. Opérateur d’assignation
    3. Destructeur

    Toutefois, ne pas inclure le constructeur de déplacement ou l’opérateur d’assignation de mouvement n’implique pas un bogue. Il peut s’agir d’une opportunité manquée lors de l’optimisation (dans la plupart des cas) ou que la sémantique de déplacement n’est pas pertinente pour cette classe, mais ce n’est pas un bogue.

    Bien qu’il soit préférable de définir un constructeur de déplacement lorsque cela est pertinent, ce n’est pas obligatoire. Il y a beaucoup de cas où un constructeur de déplacement n’est pas pertinent pour une classe (par exemple, std::complex ) et toutes les classes qui se comportent correctement dans C ++ 03 continueront à se comporter correctement dans C ++ 0x, même si elles ne le font pas. définir un constructeur de déplacement.

    Oui, je pense que ce serait bien de fournir un constructeur de déménagement pour ces classes, mais souvenez-vous que:

    • C’est seulement une optimisation.

      L’implémentation d’un seul ou deux constructeurs de copie, d’opérateurs d’affectation ou de destructeurs entraînera probablement des bogues, alors que le fait de ne pas avoir de constructeur de déplacement réduira potentiellement les performances.

    • Le constructeur de déplacement ne peut pas toujours être appliqué sans modifications.

      Certaines classes ont toujours leurs pointeurs alloués, et ces classes suppriment donc toujours leurs pointeurs dans le destructeur. Dans ces cas, vous devrez append des vérifications supplémentaires pour indiquer si leurs pointeurs sont alloués ou ont été déplacés (sont maintenant nuls).

    Voici une brève mise à jour sur le statut actuel et les développements connexes depuis le 24 janvier 2011.

    Selon le standard C ++ 11 (voir Annexe D’s [depr.impldec]):

    La déclaration implicite d’un constructeur de copie est obsolète si la classe possède un opérateur d’affectation de copie déclaré par l’utilisateur ou un destructeur déclaré par l’utilisateur. La déclaration implicite d’un opérateur d’assignation de copie est déconseillée si la classe possède un constructeur de copie déclaré par l’utilisateur ou un destructeur déclaré par l’utilisateur.

    Il a en fait été proposé de rendre obsolète le comportement obsolète donnant à C ++ 14 une véritable «règle de cinq» au lieu de la «règle des trois» traditionnelle. En 2013, l’EWG a voté contre cette proposition qui sera mise en œuvre en C ++ 2014. La principale justification de la décision concernant la proposition concernait les préoccupations générales relatives à la rupture du code existant.

    Récemment, il a été proposé de nouveau d’adapter le libellé du C ++ 11 de manière à atteindre la règle informelle des cinq règles, à savoir que

    Aucune fonction de copie, fonction de déplacement ou destructeur ne peut être généré par le compilateur si l’une de ces fonctions est fournie par l’utilisateur.

    Si elle est approuvée par l’EWG, la “règle” est susceptible d’être adoptée pour C ++ 17.

    En gros, c’est comme ceci: si vous ne déclarez aucune opération de déplacement, vous devez respecter la règle des trois. Si vous déclarez une opération de déplacement, il n’y a pas de mal à “violer” la règle des trois car la génération d’opérations générées par le compilateur est devenue très ressortingctive. Même si vous ne déclarez pas les opérations de déplacement et que vous ne respectez pas la règle des trois, un compilateur C ++ 0x est censé vous avertir si une fonction spéciale a été déclarée par l’utilisateur et que d’autres fonctions spéciales ont été générées automatiquement désormais obsolète “règle de compatibilité C ++ 03”.

    Je pense qu’il est prudent de dire que cette règle devient un peu moins importante. Le vrai problème en C ++ 03 est que l’implémentation de différentes sémantiques de copie nécessitait que l’utilisateur déclare toutes les fonctions spéciales associées afin qu’aucune d’entre elles ne soit générée par le compilateur (ce qui ne serait pas le cas). Mais C ++ 0x modifie les règles relatives à la génération de fonctions membres spéciales. Si l’utilisateur déclare une seule de ces fonctions pour modifier la sémantique de la copie, il empêchera le compilateur de générer automatiquement les fonctions spéciales restantes. C’est une bonne chose car une déclaration manquante transforme une erreur d’exécution en une erreur de compilation (ou au moins un avertissement). En tant que mesure de compatibilité C ++ 03, certaines opérations sont toujours générées, mais cette génération est considérée comme obsolète et devrait au moins produire un avertissement en mode C ++ 0x.

    En raison des règles plutôt ressortingctives concernant les fonctions spéciales générées par le compilateur et la compatibilité C ++ 03, la règle de trois rest la règle de trois.

    Voici quelques exemples qui devraient convenir aux dernières règles C ++ 0x:

     template class unique_ptr { T* ptr; public: explicit unique_ptr(T* p=0) : ptr(p) {} ~unique_ptr(); unique_ptr(unique_ptr&&); unique_ptr& operator=(unique_ptr&&); }; 

    Dans l’exemple ci-dessus, il n’est pas nécessaire de déclarer l’une des autres fonctions spéciales supprimées. Ils ne seront tout simplement pas générés en raison des règles ressortingctives. La présence d’opérations de déplacement déclarées par l’utilisateur désactive les opérations de copie générées par le compilateur. Mais dans un cas comme celui-ci:

     template class scoped_ptr { T* ptr; public: explicit scoped_ptr(T* p=0) : ptr(p) {} ~scoped_ptr(); }; 

    Un compilateur C ++ 0x est maintenant censé générer un avertissement sur les opérations de copie éventuellement générées par le compilateur qui pourraient ne pas fonctionner correctement. Ici, la règle de trois questions et devrait être respectée. Un avertissement dans ce cas est totalement approprié et donne à l’utilisateur la possibilité de gérer le bogue. Nous pouvons supprimer le problème via des fonctions supprimées:

     template class scoped_ptr { T* ptr; public: explicit scoped_ptr(T* p=0) : ptr(p) {} ~scoped_ptr(); scoped_ptr(scoped_ptr const&) = delete; scoped_ptr& operator=(scoped_ptr const&) = delete; }; 

    Donc, la règle de trois s’applique toujours ici simplement en raison de la compatibilité C ++ 03.

    Nous ne pouvons pas dire que la règle de 3 devient la règle de 4 (ou 5) maintenant sans casser tout code existant qui applique la règle de 3 et ne met en œuvre aucune forme de sémantique de mouvement.

    Règle de 3 signifie que si vous en implémentez un, vous devez tous les implémenter.

    Aussi pas au courant qu’il y aura un mouvement généré automatiquement. Le but de “règle de 3” est qu’ils existent automatiquement et que si vous en implémentez un, l’implémentation par défaut des deux autres est incorrecte.

    Dans le cas général, alors oui, la règle des trois est simplement passée à cinq, avec l’opérateur d’affectation de mouvement et le constructeur de déplacement ajoutés. Cependant, toutes les classes ne sont pas copiables et mobiles, certaines sont simplement copiables.