Ma compréhension est qu'en C ++ 11, lorsque vous renvoyez une variable locale d'une fonction par valeur, le compilateur est autorisé à traiter cette variable comme une référence de valeur r et à la `` déplacer '' hors de la fonction pour la renvoyer (si RVO / NRVO ne se produit pas à la place, bien sûr).
Ma question est, cela ne peut-il pas casser le code existant?
Considérez le code suivant:
#include <iostream>
#include <string>
struct bar
{
bar(const std::string& str) : _str(str) {}
bar(const bar&) = delete;
bar(bar&& other) : _str(std::move(other._str)) {other._str = "Stolen";}
void print() {std::cout << _str << std::endl;}
std::string _str;
};
struct foo
{
foo(bar& b) : _b(b) {}
~foo() {_b.print();}
bar& _b;
};
bar foobar()
{
bar b("Hello, World!");
foo f(b);
return std::move(b);
}
int main()
{
foobar();
return EXIT_SUCCESS;
}
Mes pensées étaient qu'il serait possible pour un destructeur d'un objet local de référencer l'objet qui se déplace implicitement, et donc de voir de manière inattendue un objet «vide». J'ai essayé de tester cela (voir http://ideone.com/ZURoeT ), mais j'ai obtenu le résultat «correct» sans l'explicite std::move
dans foobar()
. Je suppose que c'était dû à NRVO, mais je n'ai pas essayé de réorganiser le code pour le désactiver.
Ai-je raison de dire que cette transformation (provoquant un déplacement hors de la fonction) se produit implicitement et pourrait casser le code existant?
MISE À JOUR Voici un exemple qui illustre ce dont je parle. Les deux liens suivants sont pour le même code. http://ideone.com/4GFIRu - C ++ 03 http://ideone.com/FcL2Xj - C ++ 11
Si vous regardez la sortie, c'est différent.
Donc, je suppose que cette question devient maintenant, a-t-elle été prise en compte lors de l'ajout d'un déplacement implicite à la norme, et il a été décidé qu'il était OK d'ajouter ce changement de rupture car ce type de code est assez rare? Je me demande également si des compilateurs avertiront dans des cas comme celui-ci ...
Réponses:
Scott Meyers a posté sur comp.lang.c ++ (août 2010) un problème où la génération implicite de constructeurs de mouvement pouvait casser les invariants de classe C ++ 03:
Ici, le problème est qu'en C ++ 03,
X
avait un invariant que sonv
membre avait toujours 5 éléments.X::~X()
comptait sur cet invariant, mais le constructeur de mouvement nouvellement introduit est passé dev
, mettant ainsi sa longueur à zéro.Cela est lié à votre exemple car l'invariant cassé n'est détecté que dans le
X
destructeur de (comme vous le dites, il est possible pour un destructeur d'un objet local de référencer l'objet qui est implicitement déplacé, et donc de voir de manière inattendue un objet vide ).C ++ 11 essaie de trouver un équilibre entre casser une partie du code existant et fournir des optimisations utiles basées sur des constructeurs de déplacement.
Le comité a initialement décidé que les constructeurs de déplacement et les opérateurs d'affectation de déplacement devraient être générés par le compilateur lorsqu'ils ne sont pas fournis par l'utilisateur.
Puis a décidé que cela était en effet alarmant et a limité la génération automatique de constructeurs de mouvements et d'opérateurs d'affectation de mouvements de telle sorte qu'il est beaucoup moins probable, mais pas impossible, que le code existant se casse (par exemple, destructeur défini explicitement).
Il est tentant de penser qu'il est suffisant d'empêcher la génération de constructeurs de mouvements implicites lorsqu'un destructeur défini par l'utilisateur est présent ( N3153 - Implicit Move Must Go pour plus de détails).
Dans N3174 - Déplacer ou ne pas déplacer Stroupstrup dit:
la source