RVO fonctionne-t-il sur les membres d’object?

Considérer ce qui suit:

struct A { /* ... */ }; A foo() { auto p = std::make_pair(A{}, 2); // ... do something return p.first; } auto a = foo(); 

Est-ce que p.first sera copié, déplacé ou RVO-ed?

    J’ai trouvé dans Visual Studio 2010 et dans gcc-5.1, RVO n’est pas appliqué (voir, par exemple, http://coliru.stacked-crooked.com/a/17666dd9e532da76 ).

    La section pertinente de la norme est 12.8.31.1 [class.copy]. Il stipule que la copie est autorisée (en soulignant):

    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 (autre qu’un paramètre de fonction ou une variable introduite par la déclaration d’exception d’un gestionnaire ([except.handle] )) avec le même type (en ignorant cv-qualification) que le type de retour de la fonction, l’opération de copie / déplacement peut être omise en construisant l’object automatique directement dans la valeur de retour de la fonction

    Puisque p.first n’est pas le nom d’un object, RVO est interdit.

    Juste pour append un peu plus de carburant, comment cela fonctionnerait-il si RVO était en jeu? L’appelant a mis une instance de A quelque part en mémoire, puis appelle foo pour l’assigner (encore mieux, supposons que A faisait partie d’une structure plus grande et supposons qu’elle soit correctement alignée, de sorte que le prochain membre de la structure est immédiatement après l’instance de A ). En supposant que RVO soit en jeu, la first partie de p est située à l’endroit où l’appelant le voulait, mais où est placé le second int ? Il doit être placé juste après l’instance de A pour que la pair fonctionne correctement, mais à l’emplacement source, il existe un autre membre juste après l’instance de A

    Je m’attendrais à ce que RVO ne se produise pas dans cet endroit, car vous ne rapportez qu’une partie d’un object plus volumineux. Un mouvement pourrait se produire car il faudrait tout d’abord laisser le système dans un état destructible.

    @atkins est arrivé en premier avec la réponse. Ajoutez simplement ce petit programme de test que vous jugerez peut-être utile à l’avenir lors du suivi du comportement de déplacement / d’assignation.

     #include  #include  using namespace std::ssortingng_literals; struct A { A() : history("created") { } A(A&& r) : history("move-constructed,"s + r.history) { r.history = "zombie: was "s + r.history; } A(const A& r) : history("copied from: " + r.history) { } ~A() { history = "destroyed,"s + history; std::cout << history << std::endl; } A& operator=(A&& r) { history = "move-assigned from " + r.history + " (was "s + history + ")"s; r.history = "zombie: was "s + r.history; return *this; } A& operator=(const A&r ) { history = "copied from " + r.history; return *this; } std::string history; }; A foo() { auto p = std::make_pair(A{}, 2); // ... do something return p.first; } auto main() -> int { auto a = foo(); return 0; } 

    exemple de sortie:

     destroyed,zombie: was created destroyed,move-constructed,created destroyed,copied from: move-constructed,created 

    Considérons le code suivant:

     struct A {}; struct B {}; struct C { B c[100000]; }; A callee() { struct S { A a; C c; } s; return sa; } void caller() { A a = callee(); // here should lie free unused spacer of size B[100000] B b; } 

    Un RVO “partiel” devrait entraîner un gonflement excessif de la stack de l’appelant, car (je pense) que S ne peut être construit que dans le cadre de la stack de l’appelant.

    Le comportement de ~S() problème:

     // a.hpp struct A {}; struct B {}; struct C { A a; B b; ~C(); }; // a.cpp #include "a.hpp" ~C() { /* ... */; } // main.cpp #include "a.hpp" A callee() { C c; return ca; } // How to destruct c partially, having the user defined ~C() in another TU? // Even if destructor is inline and its body is visible, // how to automatically change its logic properly? // It is impossible in general case. void caller() { A a = callee(); }