Pourquoi ne std::move empêcher RVO?
Dans de nombreux cas de retour d'un local à partir d'une fonction, RVO coups de pied dans. Cependant, je pensais que l'utilisation explicite std::move
permettrait au moins de faire respecter le déplacement lors de la RVO ne se fait pas, mais que RVO est toujours en vigueur, lorsque cela est possible. Cependant, il semble que ce n'est pas le cas.
#include "iostream"
class HeavyWeight
{
public:
HeavyWeight()
{
std::cout << "ctor" << std::endl;
}
HeavyWeight(const HeavyWeight& other)
{
std::cout << "copy" << std::endl;
}
HeavyWeight(HeavyWeight&& other)
{
std::cout << "move" << std::endl;
}
};
HeavyWeight MakeHeavy()
{
HeavyWeight heavy;
return heavy;
}
int main()
{
auto heavy = MakeHeavy();
return 0;
}
J'ai testé ce code avec VC++11 et GCC 4.71, debug et release (-O2
) config. La copie ctor n'est jamais appelée. Le mouvement ctor est appelé par VC++11 dans le debug config. En fait, tout semble aller pour le mieux avec ces compilateurs en particulier, mais à ma connaissance, RVO est facultatif.
Cependant, si je utiliser explicitement move
:
HeavyWeight MakeHeavy()
{
HeavyWeight heavy;
return std::move(heavy);
}
le déplacer ctor est toujours appelée. Donc, en essayant de la rendre "sûr", il est pire.
Mes questions sont les suivantes:
- Pourquoi ne std::move
empêcher RVO?
- Quand est-il préférable de "l'espoir pour le meilleur", et s'appuient sur RVO, et quand dois-je utiliser explicitement std::move
? Ou, en d'autres termes, comment puis-je laisser le compilateur d'optimisation de faire son travail et toujours appliquer déplacer si RVO n'est pas appliquée?
- Pourquoi toujours parler de "l'espoir pour le meilleur" ces jours-ci? Quel type de compilateur utilisent-ils qui a le C++11, mais ne peut pas RVO correctement?
- Copie élision (le mécanisme derrière la RVO) est autorisée que dans certaines, des conditions strictes. Écrit
std::move
empêche que ces conditions soient remplies. - Et ces conditions empêché par std::move sont...?
- std::move empêche
NRVO
(nomméRVO
).RVO
en général n'est pas affecté (autant que je sache) - Pouvez-vous indiquer une référence décrivant le raisonnement derrière empêcher cela? Ne déplacez élision jamais arriver?
- Nope, retour std::move(poids Lourd()); appelle encore les déplacer ctor.
- Déplacer élision ne se produise, mais pas quand explicitement en utilisant std::move. De drôles de trucs.
- J'ai pensé que j'étais juste d'être approfondie en faisant explicite std::move est, au contraire il me semble que j'ai été le sabotage moi-même! 😉
- Je n'ai pas dit que le compilateur ne
RVO
dans lestd::move
cas. J'ai seulement dit, qu'ils ne peuvent pas faireNRVO
, mais (comme je l'ai dit, autant que je sache) peut encore faireRVO
. Il est dépendant de l'implémentation. - Vous n'êtes pas seul.
- Le déménagement peut toujours être ramené si le compilateur peut prouver qu'il ne change pas le programme du comportement.
- Le problème des cas est celui où le changement de comportement est admise, à savoir l'omission de copier/déplacer constructeur appelle. Depuis le cas de test, par définition, doit contenir les effets secondaires, vous êtes limité à des optimisations qui s'appuient sur une copie élision et les règles du jeu.
Vous devez vous connecter pour publier un commentaire.
Le cas où copier et déplacer des élision est permis est trouvé dans la section 12.8 §31 de la Norme (version N3690):
(Les deux cas, j'ai laissé en dehors du cas de le lancer et attraper des objets d'exception, que je considère comme moins important pour l'optimisation.)
Donc dans une instruction de retour de la copie élision ne peut se faire que si l'expression est le nom d'une variable locale. Si vous écrivez
std::move(var)
, alors il n'est pas le nom d'une variable de plus. Donc le compilateur ne peut pas éluder le déplacement, si elle doit être conforme à la norme.Stephan T. Lavavej parlé à Natifs 2013 et expliqué exactement votre situation et pourquoi faut-il éviter
std::move()
ici. Commencer à regarder à la minute 38:04. En gros, lors du retour d'une variable locale de type de retour alors qu'il est généralement traitée comme une rvalue permettant de déplacer par défaut.return std::move
peuvent être gommés. Si nous pourrions dire que le C++ qui est la référence la valeur de retour d'une fonction est garanti d'être le même que celui d'un particulier de référence de la valeur d'entrée de la fonction, à quelques résultats intéressants pourraient s'ouvrir. (Élision de non-trivial expressions, la durée de vie de l'extension temporaire d'un argument d'entrée à une fonction sans retour temporaire, pour les deux: le deuxième de ce qui est à mon humble avis plus important).Comme ceci:
Transformant le retour à un déplacement est obligatoire.
return std::move
😉move
dans le pire des cas. Quelque chose d'aussi anodin quereturn condition?heavy1:heavy2;
peut empêcher l'implicitemove
, tandis queif (condition) return heavy1; return heavy2;
ne le serait pas. Il est un peu fragile.