Pourquoi aucune affectation par déplacement / constructeur de déplacement par défaut?

Je suis un simple programmeur. Les variables de mes membres de classe se composent le plus souvent de types POD et de conteneurs STL. Pour cette raison, je dois rarement écrire des opérateurs d’affectation ou copier des constructeurs, car ceux-ci sont implémentés par défaut.

Ajoutez à cela, si j’utilise std::move sur des objects non mobiles, il utilise l’opérateur d’assignation, ce qui signifie que std::move est parfaitement sûr.

Comme je suis un simple programmeur, j’aimerais tirer parti des capacités de déplacement sans append un opérateur de déplacement / constructeur d’affectation à chaque classe que j’écris, car le compilateur pourrait simplement les implémenter sous la forme ” this->member1_ = std::move(other.member1_);...

Mais ce n’est pas le cas (du moins pas dans Visual 2010), y a-t-il une raison particulière à cela?

Plus important; y a-t-il un moyen de contourner ce problème?

Mise à jour: Si vous regardez la réponse de GManNickG, il fournit une excellente macro pour cela. Et si vous ne le saviez pas, si vous implémentez move-semantics, vous pouvez supprimer la fonction de membre de swap.

La génération implicite des constructeurs de déplacement et des opérateurs d’affectation a été controversée et les versions récentes du standard C ++ ont fait l’object de révisions majeures, de sorte que les compilateurs actuellement disponibles se comporteront probablement différemment par rapport à la génération implicite.

Pour en savoir plus sur l’historique du problème, consultez la liste des articles 2010 du WG21 et recherchez «mov».

La spécification actuelle (N3225, de novembre) indique (N3225 12.8 / 8):

Si la définition d’une classe X ne déclare pas explicitement un constructeur de déplacement, on sera implicitement déclaré comme étant par défaut si et seulement si

  • X n’a pas de constructeur de copie déclaré par l’utilisateur, et

  • X n’a pas d’opérateur d’affectation de copie déclaré par l’utilisateur,

  • X n’a pas d’opérateur d’affectation de mouvement déclaré par l’utilisateur,

  • X n’a pas de destructeur déclaré par l’utilisateur, et

  • le constructeur de déplacement ne serait pas implicitement défini comme supprimé.

Il y a un langage similaire au 12.8 / 22 en spécifiant quand l’opérateur d’assignation de déplacement est déclaré implicitement comme étant par défaut. Vous pouvez trouver la liste complète des modifications apscopes pour prendre en charge la spécification actuelle de la génération de mouvements implicites dans N3203: Renforcer les conditions de génération des mouvements implicites

Les constructeurs de déplacements générés implicitement ont été pris en compte pour la norme, mais peuvent être dangereux. Voir l’ parsing de Dave Abrahams.

Au final, cependant, la norme incluait la génération implicite de constructeurs de déplacements et d’opérateurs d’affectation de mouvements, avec cependant une liste assez importante de limitations:

Si la définition d’une classe X ne déclare pas explicitement un constructeur de déplacement, on sera implicitement déclaré comme étant par défaut si et seulement si
– X n’a ​​pas de constructeur de copie déclaré par l’utilisateur,
– X n’a ​​pas d’opérateur d’affectation de copie déclaré par l’utilisateur,
– X n’a ​​pas d’opérateur d’affectation de mouvement déclaré par l’utilisateur,
– X n’a pas de destructeur déclaré par l’utilisateur, et
– le constructeur de déplacement ne serait pas implicitement défini comme supprimé.

Ce n’est pas tout ce qu’il y a dans l’histoire. Un moteur peut être déclaré, mais toujours défini comme supprimé:

Un constructeur de copie / déplacement déclaré implicitement est un membre public en ligne de sa classe. Un constructeur de copie / déplacement par défaut pour une classe X est défini comme supprimé (8.4.3) si X a:

– un membre variant avec un constructeur correspondant non sortingvial et X est une classe de type union,
– un membre de données non statique de classe type M (ou tableau de celui-ci) qui ne peut être copié / déplacé car la résolution de surcharge (13.3) appliquée au constructeur correspondant de M entraîne une ambiguïté ou une fonction supprimée ou inaccessible constructeur par défaut,
– une classe de base directe ou virtuelle B qui ne peut pas être copiée / déplacée car la résolution de la surcharge (13.3), appliquée au constructeur correspondant de B, entraîne une ambiguïté ou une fonction supprimée ou inaccessible du constructeur par défaut,
– toute classe de base directe ou virtuelle ou membre de données non statique d’un type avec un destructeur supprimé ou inaccessible du constructeur par défaut,
– pour le constructeur de copie, un membre de données non statique de type référence rvalue, ou
– pour le constructeur de déplacement, un membre de données non statique ou une classe de base directe ou virtuelle avec un type qui n’a pas de constructeur de déplacement et qui n’est pas sortingvialement copiable.

(pour l’instant, je travaille sur une macro stupide …)

Oui, je suis aussi allé dans cette voie. Voici votre macro:

 // detail/move_default.hpp #ifndef UTILITY_DETAIL_MOVE_DEFAULT_HPP #define UTILITY_DETAIL_MOVE_DEFAULT_HPP #include  #define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE(pR, pData, pBase) pBase(std::move(pOther)) #define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE(pR, pData, pBase) pBase::operator=(std::move(pOther)); #define UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR(pR, pData, pMember) pMember(std::move(pOther.pMember)) #define UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT(pR, pData, pMember) pMember = std::move(pOther.pMember); #define UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers) \ pT(pT&& pOther) : \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases)) \ , \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers)) \ {} \ \ pT& operator=(pT&& pOther) \ { \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases) \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers) \ \ return *this; \ } #define UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases) \ pT(pT&& pOther) : \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR_BASE, BOOST_PP_EMPTY, pBases)) \ {} \ \ pT& operator=(pT&& pOther) \ { \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT_BASE, BOOST_PP_EMPTY, pBases) \ \ return *this; \ } #define UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers) \ pT(pT&& pOther) : \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM( \ UTILITY_MOVE_DEFAULT_DETAIL_CONSTRUCTOR, BOOST_PP_EMPTY, pMembers)) \ {} \ \ pT& operator=(pT&& pOther) \ { \ BOOST_PP_SEQ_FOR_EACH(UTILITY_MOVE_DEFAULT_DETAIL_ASSIGNMENT, BOOST_PP_EMPTY, pMembers) \ \ return *this; \ } #endif 

 // move_default.hpp #ifndef UTILITY_MOVE_DEFAULT_HPP #define UTILITY_MOVE_DEFAULT_HPP #include "utility/detail/move_default.hpp" // move bases and members #define UTILITY_MOVE_DEFAULT(pT, pBases, pMembers) UTILITY_MOVE_DEFAULT_DETAIL(pT, pBases, pMembers) // base only version #define UTILITY_MOVE_DEFAULT_BASES(pT, pBases) UTILITY_MOVE_DEFAULT_BASES_DETAIL(pT, pBases) // member only version #define UTILITY_MOVE_DEFAULT_MEMBERS(pT, pMembers) UTILITY_MOVE_DEFAULT_MEMBERS_DETAIL(pT, pMembers) #endif 

(J’ai enlevé les vrais commentaires, qui sont la longueur et le documentaire.)

Vous spécifiez les bases et / ou les membres de votre classe en tant que liste de préprocesseur, par exemple:

 #include "move_default.hpp" struct foo { UTILITY_MOVE_DEFAULT_MEMBERS(foo, (x)(str)); int x; std::ssortingng str; }; struct bar : foo, baz { UTILITY_MOVE_DEFAULT_BASES(bar, (foo)(baz)); }; struct baz : bar { UTILITY_MOVE_DEFAULT(baz, (bar), (ptr)); void* ptr; }; 

Et sort un opérateur de déplacement-constructeur et d’affectation de mouvement.

(En passant, si quelqu’un sait comment je pourrais combiner les détails en une seule macro, ce serait un gonflement.)

VS2010 ne le fait pas car ils n’étaient pas standard au moment de la mise en œuvre.