c++ - developpement - conception et réalisation d une application de gestion des établissements scolaires pdf




Pourquoi le constructeur de mouvements défini par l'utilisateur désactive le constructeur de copies implicite? (2)

Pendant que je lis boost / shared_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 "constructeur de copie généré, destructeur est bien sauf en C ++ 11, déplacer désactive la copie implicite" signifie ici? Doit-on toujours écrire le moteur de copie pour éviter cette situation en C ++ 11?


J'ai voté la réponse de ildjarn parce que je l'ai trouvée à la fois précise et humoristique. :-)

Je propose une autre réponse parce que je suppose que, en raison du titre de la question, le PO pourrait vouloir savoir pourquoi la norme le dit.

Contexte

C ++ a implicitement généré des membres de copie car si ce n'était pas le cas, il serait toujours né en 1985 parce qu'il était tellement incompatible avec C. Et dans ce cas, nous n'aurions pas cette conversation aujourd'hui car C ++ n'existerait pas .

Cela étant dit, les membres de la copie générés implicitement sont apparentés à un "accord avec le diable". C ++ n'aurait pas pu être né sans eux. Mais ils sont mauvais en ce sens qu'ils génèrent silencieusement du code incorrect dans un nombre significatif d'instances. Le comité C ++ n'est pas stupide, ils le savent.

C ++ 11

Maintenant que C ++ est né et a évolué pour devenir un adulte prospère, le comité adorerait dire: nous ne faisons plus de membres de copie générés implicitement. Ils sont trop dangereux. Si vous voulez un membre de copie généré implicitement, vous devez vous inscrire à cette décision (et non vous en désinscrire). Cependant, compte tenu de la quantité de code C ++ existante qui se briserait si cela était fait, cela équivaudrait à un suicide. Il existe un énorme problème de compatibilité ascendante qui est tout à fait justifié.

Le comité a donc atteint une position de compromis: si vous déclarez des membres en mouvement (ce que le code C ++ hérité ne peut pas faire), nous allons supposer que les membres de 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 commune (par exemple, unique_ptr , ofstream , future , etc.). Et le coût de l'opt-in est en fait assez petit avec = default .

Avoir hâte de

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

Conclusion

Alors soyez prévenus: C ++ a grandi et mûri au fil des décennies. Et cela signifie que le C ++ de votre père peut nécessiter une migration pour gérer le C ++ de votre enfant. Il s’agit d’un processus lent et progressif, qui vous permet de ne pas lever les bras et de ne pas utiliser une autre langue. Mais c'est le changement, même si c'est lent.


Parce que le standard C ++ le dit - §12.8 / 7:

Si la définition de classe ne déclare pas explicitement un constructeur de copie, l'un d'eux est déclaré implicitement . Si la définition de classe déclare un constructeur de déplacement ou un opérateur d'affectation de déplacement, le constructeur de copie déclaré implicitement est défini comme étant supprimé ; sinon, il est défini comme étant par défaut. Ce dernier cas est déconseillé 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. Ainsi, pour la définition de 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) { /* ... */ }

alors toute utilisation du constructeur de copie de X est mal formée à cause de l'ambiguïté; aucun diagnostic n'est requis.

(Soulignez le mien.)







rvalue-reference