Conditions pour la copie de copie?

Je voulais vérifier si les optimisations suivantes fonctionnaient comme prévu:

  • RVO
  • Nommé RVO
  • Copier elision en passant un argument par valeur

Alors j’ai écrit ce petit programme:

#include  #include  #include  #include  struct Foo { Foo(std::size_t length, char value) : data(length, value) { } Foo(const Foo & rhs) : data(rhs.data) { std::cout << "*** COPY ***" << std::endl; } Foo & operator= (Foo rhs) { std::cout << "*** ASSIGNMENT ***" << std::endl; std::swap(data, rhs.data); // probably expensive, ignore this please return *this; } ~Foo() { } std::vector data; }; Foo TestRVO() { return Foo(512, 'r'); } Foo TestNamedRVO() { Foo result(512, 'n'); return result; } void PassByValue(Foo inFoo) {} int main() { std::cout << "\nTest RVO: " << std::endl; Foo rvo = TestRVO(); std::cout << "\nTest named RVO: " << std::endl; Foo named_rvo = TestNamedRVO(); std::cout << "\nTest PassByValue: " << std::endl; Foo foo(512, 'a'); PassByValue(foo); std::cout << "\nTest assignment: " << std::endl; Foo f(512, 'f'); Foo g(512, 'g'); f = g; } 

Et je l’ai compilé avec les optimisations activées:

 $ g++ -o test -O3 main.cpp ; ./test 

Ceci est la sortie:

 Test RVO: Test named RVO: Test PassByValue: *** COPY *** Test assignment: *** COPY *** *** ASSIGNMENT *** 

Selon la sortie RVO et RVO nommé fonctionnent comme prévu. Toutefois, la copie n’est pas effectuée pour l’opérateur d’affectation et lors de l’appel de PassByValue .

La suppression de copie n’est-elle pas autorisée sur les constructeurs de copie définis par l’utilisateur? (Je sais que RVO est explicitement autorisé par la norme, mais je ne sais pas comment élision de copie lorsque je passe par valeur.) Existe-t-il un moyen de vérifier l’élision de copie sans définir de constructeur de copie?

La manière dont vous utilisez le constructeur de copie ne peut pas être supprimée, car l’object copié existe toujours après l’appel.

Si vous essayez de cette façon, cela pourrait fonctionner mieux:

 PassByValue(Foo(512, 'a')); 

Toutes les optimisations sont autorisées mais pas obligatoires, il appartient donc à chaque compilateur de décider de ce qu’il peut et de ce qu’il fera.

La norme dit (au paragraphe 12.8.15):

Cette élision des opérations de copie est autorisée dans les cas suivants (qui peuvent être combinés pour éliminer les copies multiples):

  • dans une instruction return dans une fonction avec un type de retour de classe, lorsque l’expression est le nom d’un object automatique non volatile avec le même type cv-non qualifié que le type de retour de fonction, l’opération de copie peut être omise en construisant l’object automatique directement dans la valeur de retour de la fonction

  • lorsqu’un object de classe temporaire qui n’a pas été lié à une référence (12.2) serait copié dans un object de classe avec le même type cv-non qualifié, l’opération de copie peut être omise en construisant l’object temporaire directement dans la cible du fichier. copie omise

Aucun de ces cas ne s’applique ici, donc l’élision n’est pas autorisée. Le premier est évident (pas de retour). La seconde n’est pas autorisée, car l’object que vous transmettez n’est pas temporaire.

Notez que votre code est toujours correct, car vous devrez quand même créer la copie. Pour supprimer cette copie, vous devez utiliser la sémantique de déplacement de C ++ 0x.