web-dev-qa-db-fra.com

Pourquoi le constructeur de déplacement défini par l'utilisateur désactive le constructeur de copie implicite?

Pendant que je lisez Boost/Shared_PTR.PTR.HPP, j'ai vu ce code:

//  generated copy constructor, destructor are fine...

#if defined( BOOST_HAS_RVALUE_REFS )

// ... except in C++0x, move disables the implicit copy

shared_ptr( shared_ptr const & r ): px( r.px ), pn( r.pn ) // never throws
{
}

#endif

Qu'est-ce que le commentaire "Copier Copy Cooper, Destructor va bien sauf en C++ 11, le déplacement désactive la copie implicite" signifie ici? Allons-nous toujours écrire la copie CTOR nous-mêmes pour empêcher cette situation en C++ 11?

40
amazingjxq

J'ai évoqué la réponse de Ildjarn parce que je l'ai trouvé exacte et humoristique. :-)

Je fournis une réponse alternative car je suppose que je suppose en raison du titre de la question que l'OP pourrait vouloir savoir pourquoi La norme le dit.

arrière-plan

C++ a implicitement généré des membres de la copie, car si ce n'est pas le cas, il aurait été néanmoins né en 1985 car il était SO incompatible avec C. et dans ce cas, nous ne recevrions pas cette conversation aujourd'hui parce que c ++ n'existerait pas.

Cela étant dit, les membres de la copie générée implicitement s'apparaissent à une "affaire avec le diable". C++ n'aurait pas pu être né sans eux. Mais ils sont diaboliques dans lesquels ils génèrent silencieusement un code incorrect dans un nombre important d'instances. Le comité C++ n'est pas stupide, ils le savent.

C++ 11

Maintenant que C++ est né et a évolué dans une adulte réussie, le comité aimerait simplement dire: nous ne faisions pas plus que les membres de la copie générée implicitement. Ils sont trop dangereux. Si vous souhaitez un membre de copie généré implicitement, vous devez vous inscrire à cette décision (par opposition à la reprise de celui-ci). Cependant, compte tenu de la quantité de code C++ existant qui briserait si cela a été fait, cela reviendrait au suicide. Il y a un énorme une préoccupation de compatibilité à l'envers qui est assez justifiée.

Le comité a donc atteint un poste de compromis: si vous déclarez que les membres du déplacement (quel code legacy c ++ ne peut pas faire), nous allons supposer que les membres de la copie par défaut sont susceptibles de faire la mauvaise chose. Opt-in (avec =default) si vous le souhaitez. Ou écrivez-les vous-même. Sinon, ils sont implicitement supprimés. Notre expérience à ce jour dans un monde avec des types de déplacement uniquement indique que cette position par défaut est en réalité assez couramment ce qui est souhaité (par exemple, unique_ptr, ofstream, future, etc. .). Et les frais d'optage sont en fait assez petit avec = default.

hâte

Le comité aimerait même dire: Si vous avez écrit un destructeur, il est probable que les membres de la copie implicite soient incorrects, nous allons donc les supprimer. Ceci est la règle de trois "de trois" C++ 98/03. Cependant, même cela briserait beaucoup de code. Cependant, le comité a dit en C++ 11 que si vous fournissez un destructeur déclaré par l'utilisateur, la génération implicite de membres de la copie est obsolète. Cela signifie que cette fonctionnalité pourrait être supprimée dans une norme future. Et que n'importe quel jour maintenant, votre compilateur pourrait commencer à émettre des "avertissements obsolètes" dans cette situation (la norme ne peut spécifier des avertissements).

Conclusion

Soyez donc prévenu: C++ a grandi et a augmenté au fil des décennies. Et cela signifie que le C++ de votre père peut avoir besoin de migrer pour faire face à la C++ de votre enfant. C'est un processus lent et progressif de sorte que vous ne vomissez pas vos mains et que vous venez de passer à une autre langue. Mais c'est est changer, même si lent.

102
Howard Hinnant

Parce que la norme C++ le dit: §12.8/7:

Si la définition de la classe ne déclare pas explicitement un constructeur de copie, on est déclaré implicitement implicitement . Si la définition de la classe déclare un constructeur de déplacement ou bouger l'opérateur d'affectation, le constructeur de copie déclarée implicitement est défini comme supprimé; Sinon, il est défini comme défaut. Ce dernier cas est obsolète si la classe a un opérateur d'affectation de copie déclarée par l'utilisateur ou un destructeur déclaré par l'utilisateur. Ainsi, pour la définition de la classe

struct X {
    X(const X&, int);
};

un constructeur de copie est déclaré implicitement. Si le constructeur déclaré par l'utilisateur est défini ultérieurement comme

X::X(const X& x, int i =0) { /* ... */ }

ensuite, toute utilisation du constructeur de copie X est mal formée en raison de l'ambiguïté; aucun diagnostic n'est requis.

(Mettre l'accent sur le mien.)

24
ildjarn