11
votes

Pass-by-valeur entraînant un mouvement supplémentaire

J'essaie de comprendre déplacer la sémantique et de copier / bouger élision.

Je voudrais une classe qui enveloppe certaines données. Je voudrais transmettre les données dans le constructeur et je voudrais posséder les données. P>

Après avoir lu Ceci , Ceci et Ce J'ai eu l'impression que, dans C ++ 11 si je veux stocker une copie, la valeur réussie doit être au moins aussi efficace que toute autre option (en dehors de la Problème mineure de la taille de code accrue). P>

Ensuite, si le code d'appel souhaiterait éviter une copie, elle peut en transmettre une r devalue au lieu d'une lvalue. (par exemple, utilisation STD :: Déplacer) P>

Alors je l'ai essayé: P>

1. DataWrapperWithMove:
  constructor
  move constructor
2. DataWrapperByValue:
  constructor
  move constructor
  move constructor
3. RVO:
  constructor
  move constructor


3 Réponses :


0
votes

Je crois que c'est parce que vous faites essentiellement ce code.

std::cout << "2. DataWrapperByValue:\n";  
Data d2;
DataWrapperByValue a2(Data(std::move(d2))); // Notice a Data object is constructed. 


4 commentaires

Pour être plus explicite: le premier geste provient de d2 à data et la seconde de data à data _ .


Votre exemple n'est pas précis, également DatawrapperbyValue ne prend pas de lvalue. Il déménage ou copie son argument en fonction de sa catégorie de valeur.


@Christrew, le constructeur de déplacement des données est appelé au lieu du constructeur de copie, il peut donc être correct.


@Christrew, pas de problème.



4
votes

DatawrapperbyValue :: Data _ code> est déplacé de DatawrapperbyValue :: DatawrapperbyValue (DatawrapperbyValue (Data Data) Code> S Agler DATA code> qui est déplacé de D2 code>.

Votre conclusion à la référence pass-rvalue ainsi que la version par valeur pour les cas où vous obtenez une valeur L donne la meilleure performance. Cependant, cela est largement considéré comme optimisation prématurée. Howard Hinnant ( Meilleur moyen d'écrire un constructeur d'une classe qui contient un conteneur STL en C ++ 11 ) et Sean Parent ( http://channel9.msdn.com/events/ingnative/2013/Inhériittance-s-the-base-class-of-evil ) ont tous deux noté qu'ils considèrent cette optimisation prématurée. La raison en est que les mouvements sont censés être judicieux et pour les éviter dans ce cas provoqueraient une duplication de code, surtout si vous avez plus d'une argument qui peut être une valeur R ou L. Si, par profilement ou test, vous constatez que cet acuall dégradra la performance, vous pouvez toujours ajouter facilement la référence PASS-RVALUE après le fait. p>

Un modèle utile dans un cas où vous avez besoin de performances supplémentaires est la suivante: p> xxx pré>

ici Le constructeur fait toujours la bonne chose que possible dans Mon exemple en direct: http://ideone.com/usltra p>

L'avantage de cette Le code argouruement complexe n'est probablement pas pertinent avec une seule argine, mais imaginez si votre constructeur avait 4 arguments pouvant être des valeurs R ou L, c'est bien mieux que d'écrire 16 constructeurs différents. P>

struct CompositeWrapperByMoveOrCopy {
  Data data_;
  Foo foo_;
  Bar bar_;
  Baz baz_;
  template<typename T, typename U, typename V, typename W, 
    typename = typename std::enable_if<
        std::is_same<typename std::decay<T>::type, Data>::value &&
        std::is_same<typename std::decay<U>::type, Foo>::value &&
        std::is_same<typename std::decay<V>::type, Bar>::value &&
        std::is_same<typename std::decay<W>::type, Baz>::value
    >::type
  >
  CompositeWrapperByMoveOrCopy(T&& data, U&& foo, V&& bar, W&& baz) : 
  data_{ std::forward<T>(data) },
  foo_{ std::forward<U>(foo) },
  bar_{ std::forward<V>(bar) },
  baz_{ std::forward<W>(baz) } { }
};


0 commentaires

3
votes

DatawrapperbyValue code> a ce constructeur:

template<class U>
DataWrapperByValue(U&& u) : data_(std::forward<U>(u)) { }


5 commentaires

Vous dites que la copie élision ne peut pas se produire ici. Est-il possible d'expliquer pourquoi la copie élision est possible dans le troisième cas lorsque la rvalue (une "privale") est créée à l'aide d'une valeur de retour mais pas dans le second cas lors de sa création à l'aide de STD :: Déplacer (un "XValue") ?


@Christrew Parce que le compilateur sait que l'objet étant renvoyé n'est plus nécessaire, il est donc dans ses droits d'élier le mouvement. Pour le second cas, il ne peut pas arriver car DatawrapperbyValue (données de données) n'est pas un constructeur de déplacement ni un constructeur de copie, il s'agit simplement d'un constructeur qui prend un objet de données . En tant que tel, la copie / mouvement ne peut pas être élue.


Ne pas utiliser Sfinae pour vérifier que le type correct peut entraîner des problèmes subtils. Imaginez si quelqu'un ajoute un constructeur explicite d'un Int à la classe de données, vous pouvez construire un DatawrapperbyValue à partir d'un int, ou même un float puisque vous ne saisissez pas data_


@Porkybrain mais bien sûr que ce n'est qu'un exemple. Je ne veux pas digresser en dehors du sujet immédiat.


Assez juste, dans l'exemple à la main, il n'est pas nécessaire.