93 votes

Pourquoi pas par défaut déplacer-cession/move-constructeur?

Je suis un simple programmeur. Mes membres de la classe des variables les plus souvent constitué de POD-types et STL-conteneurs. De ce fait j'ai rarement à écrire des opérateurs d'affectation ou de copier les constructeurs, puisque celles-ci sont implémentées par défaut.

Ajoutez à cela, si j'utilise std::move sur les objets mobiliers, il utilise la cession de l'opérateur, signifiant std::move est parfaitement sûr.

Comme je suis un simple programmeur, je voudrais profiter de le déplacer-les capacités sans l'ajout d'un constructeur de déplacement/opérateur d'affectation pour chaque classe I à écrire, car le compilateur pourrait tout simplement mis en place en tant que "this->member1_ = std::move(other.member1_);..."

Mais il n'a pas (du moins pas dans Visual 2010), est-il une raison particulière à cela?

Plus important encore, est-il un moyen de contourner ce problème?

Mise à jour: Si vous regardez en bas à GManNickG de la réponse qu'il fournit une excellente macro. Et si vous ne savez pas, si vous mettez en œuvre déplacez-sémantique, vous pouvez supprimer le swap de la fonction membre.

79voto

James McNellis Points 193607

L'implicite de la génération de déplacer les constructeurs et les opérateurs d'affectation a été controversée et il y a eu des révisions majeures au cours des dernières traites de la Norme C++, donc actuellement disponibles compilateurs est susceptible de se comporter différemment à l'égard implicite génération.

Pour en savoir plus sur l'histoire de la question, voir l'2010 WG21 documents de la liste et de la recherche pour "mov"

La spécification actuelle (N3225, à partir de novembre), les états (N3225 12.8/8):

Si la définition d'une classe X ne pas déclarer explicitement un constructeur de déplacement, on va être implicitement déclarée en défaut si et seulement si

  • X n'ont pas un utilisateur déclaré constructeur de copie, et

  • X n'ont pas un utilisateur déclaré copie opérateur d'affectation,

  • X n'ont pas un utilisateur déclaré déplacer opérateur d'affectation,

  • X n'ont pas un utilisateur déclaré destructeur, et

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

Il est similaire de la langue 12,8/22 spécifiant la date de l'opérateur d'assignation de déplacement est implicitement déclaré en tant que par défaut. Vous pouvez trouver la liste complète des changements apportés à l'appui de la spécification actuelle de l'implicite de la génération des coups dans N3203: resserrer les conditions de production de l'implicite se déplace qui était basé en grande partie sur l'une des résolutions proposées par Bjarne Stroustrup du papier N3201: Déplacement à droite le long.

13voto

Jerry Coffin Points 237758

Implicitement généré des constructeurs de déplacement ont été pris en considération pour le standard, mais peut être dangereux. Voir Dave Abrahams de l' analyse.

En fin de compte, cependant, la norme ne comprennent implicite génération de déplacer les constructeurs et les opérateurs d'assignation de déplacement, mais avec une assez longue liste des limitations:

Si la définition d'une classe X ne pas déclarer explicitement un constructeur de déplacement, on va être implicitement déclarée en défaut si et seulement si
- X n'a pas un utilisateur déclaré constructeur de copie,
- X n'a pas un utilisateur déclaré copie opérateur d'affectation,
- X n'a pas un utilisateur déclaré déplacer opérateur d'affectation,
- X n'a pas un utilisateur déclaré destructeur, et
- le constructeur de déplacement ne serait pas implicitement défini comme étant supprimés.

Ce n'est pas tout à fait tout ce qu'il ya de l'histoire. Un ctor peut être déclaré, mais encore défini comme étant supprimés:

Un implicitement déclarées copier/déplacer un constructeur est une ligne membres publics de sa classe. Un défaut copier/déplacer constructeur pour une classe de X est définie comme étant supprimés (8.4.3) si X est:

- une variante de membre avec un non-trivial correspondant constructeur et X est une union-comme la classe,
- un non-membre de données statiques de la classe de type M (ou tableau de celui-ci) ne peut pas être copié/déplacé en raison de la surcharge de résolution (13.3), appliquée à la M correspondant constructeur, les résultats dans une ambiguïté ou une fonction qui est supprimé ou inaccessibles à partir de la valeur par défaut du constructeur,
- directement ou virtuel de la classe de base B ne peut pas être copié/déplacé en raison de la surcharge de résolution (13.3), appliquée à la B correspondant constructeur, les résultats dans une ambiguïté ou une fonction qui est supprimé ou inaccessibles à partir de la valeur par défaut du constructeur,
- des dommages directs ou classe de base virtuelle ou non donnée membre statique d'un type avec un destructeur qui est supprimé ou inaccessibles à partir de la valeur par défaut du constructeur,
- pour le constructeur de copie, d'un non-membre de données statiques de référence rvalue type, ou
- pour le constructeur de déplacement, un non-membre de données statiques ou direct ou virtuel de la classe de base avec un type qui n'a pas un constructeur de déplacement et n'est pas trivialement copiable.

8voto

GManNickG Points 155079

(comme pour l'instant, je travaille sur un stupide macro...)

Oui, je suis cette voie trop. Voici une macro:

// detail/move_default.hpp
#ifndef UTILITY_DETAIL_MOVE_DEFAULT_HPP
#define UTILITY_DETAIL_MOVE_DEFAULT_HPP

#include <boost/preprocessor.hpp>

#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 supprimé les commentaires réels, qui sont la longueur et le documentaire.)

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

#include "move_default.hpp"

struct foo
{
    UTILITY_MOVE_DEFAULT_MEMBERS(foo, (x)(str));

    int x;
    std::string str;
};

struct bar : foo, baz
{
    UTILITY_MOVE_DEFAULT_BASES(bar, (foo)(baz));
};

struct baz : bar
{
    UTILITY_MOVE_DEFAULT(baz, (bar), (ptr));

    void* ptr;
};

Et il en sort une déplacez-constructeur et déplacez-opérateur d'affectation.

(En aparté, si quelqu'un sait comment je pourrais combiner les détails dans une macro, ce serait de la houle.)

5voto

Puppy Points 90818

VS2010 ne le font pas parce qu'ils n'étaient pas la Norme au moment de la mise en œuvre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X