De retour unique_ptr de fonctions
unique_ptr<T>
ne permet pas la copie de la construction, au lieu de cela il prend en charge la sémantique de déplacement. Pourtant, je peux retourner un unique_ptr<T>
à partir d'une fonction et d'attribuer la valeur renvoyée à une variable.
#include <iostream>
#include <memory>
using namespace std;
unique_ptr<int> foo()
{
unique_ptr<int> p( new int(10) );
return p; //1
//return move( p ); //2
}
int main()
{
unique_ptr<int> p = foo();
cout << *p << endl;
return 0;
}
Le code ci-dessus compile et fonctionne comme prévu. Alors, comment est-ce que la ligne de 1
ne pas invoquer le constructeur de copie et entraîner des erreurs de compilation? Si je devais utiliser la ligne 2
au lieu de cela, il avait un sens (à l'aide de la ligne de 2
fonctionne aussi bien, mais nous ne sommes pas tenus de le faire).
Je sais C++0x permet à cette exception à unique_ptr
depuis la valeur de retour est un objet temporaire qui sera détruit dès que la fonction se termine, ainsi, de garantir l'unicité de le pointeur retourné. Je suis curieux de savoir comment cela est mis en œuvre, est-il spécial enfermé dans le compilateur ou est-il un autre clause dans la spécification du langage que ce exploits?
- Hypothétiquement, si vous étiez à la mise en œuvre d'une méthode de fabrique, préférez-vous 1 ou 2 pour le retour de l'usine de sortie? Je présume que ce serait l'utilisation la plus courante de 1 parce que, avec une bonne usine, vous voulez vraiment la propriété de l'construits chose à transmettre à l'appelant.
- Ils ont tous deux de transmettre la propriété de la
unique_ptr
. Toute la question est de environ 1 et 2 sont deux façons différentes de réaliser la même chose. - dans ce cas, la RVO prend place dans c++0x ainsi, la destruction de l'unique objet sera une fois qui est effectuée après
main
fonction se termine , mais pas quand lafoo
sorties.
Vous devez vous connecter pour publier un commentaire.
Oui, voir 12.8 §34 §35:
Voulais juste ajouter un point de plus que le retour par valeur devrait être le choix par défaut ici, car une valeur nommée dans l'instruction de retour dans le pire des cas, c'est à dire sans elisions en C++11 et C++14 et C++17 est traitée comme une rvalue. Ainsi, par exemple, la fonction suivante compile avec
-fno-elide-constructors
drapeauAvec le drapeau de la compilation il y a deux mouvements (1 et 2) qui se passe dans cette fonction, puis un déménagement plus tard (3).
foo()
est en effet également sur le point d'être détruite (si ce n'était pas affecté à quoi que ce soit), tout comme la valeur de retour de la fonction, et donc il est logique que le C++ utilise un constructeur de déplacement lorsque vous faitesunique_ptr<int> p = foo();
?std::unique_ptr
), il existe une règle spéciale pour la première traiter les objets comme des rvalues. Je pense que c'est d'accord entièrement avec ce que Nikola a répondu.Ce qui n'est nullement spécifique à
std::unique_ptr
, mais s'applique à toute la classe qui est mobile. Il est garanti par les règles de la langue, puisque vous êtes de retour par valeur. Le compilateur essaie d'éluder les copies, appelle un constructeur de déplacement si il ne peut pas supprimer les copies d'appeler un constructeur de copie si elle ne peut pas bouger, et ne parvient pas à compiler si il ne peut pas copier.Si vous aviez une fonction qui accepte
std::unique_ptr
comme argument, vous ne serait pas en mesure de passer de p à elle. Vous devez appeler explicitement constructeur de déplacement, mais dans ce cas, vous ne devriez pas utiliser la variable p après l'appel àbar()
.p
n'est pas temporaire, le résultat defoo()
, ce qui est retourné, est; donc c'est une rvalue et peut être déplacé, ce qui rend l'attribution demain
possible. Je dirais que vous avez été mal, sauf que Nikola semble alors appliquer cette règle àp
lui-même qui EST dans l'erreur.1
et Ligne2
? A mon avis c'est le même depuis lors de la construction dep
dansmain
, il se soucie uniquement de type de type de retour defoo
, droit?std::unique_ptr
enlevé pour être déplacer seule même RVO encore besoin d'elle?unique_ptr n'a pas le traditionnel constructeur de copie. Au lieu de cela il a un "constructeur de déplacement" qui utilise des références rvalue:
Référence rvalue (le double esperluette) ne se lient à une rvalue. C'est pourquoi vous obtenez une erreur lorsque vous essayez de passer une lvalue unique_ptr à une fonction. D'autre part, une valeur qui est renvoyée par une fonction est considérée comme une rvalue, de sorte que le constructeur de déplacement est appelé automatiquement.
Par la manière, ce sera le travail correctement:
Temporaire unique_ptr ici est une rvalue.
p
- "évidemment" un lvalue - être traité comme un rvalue dans une instruction de retourreturn p;
dans la définition defoo
. Je ne pense pas qu'il n'y a aucun problème avec le fait que la valeur de retour de la fonction elle-même peut être "déplacé".Je pense que c'est parfaitement expliqué dans à l'article 25 de Scott Meyers Moderne Et Efficace De C++. En voici un extrait:
Ici, RVO se réfère à valeur de retour d'optimisation, et si les conditions de la RVO sont remplies signifie retourner l'objet local a déclaré à l'intérieur de la fonction que vous attendez à faire de RVO, qui est aussi bien expliqué dans l'article 25 de son livre en se référant à la norme (ici le objet local comprend les objets temporaires créés par l'instruction return). Le plus important à prendre à partir de l'extrait est copier élision ou
std::move
est implicitement appliqué aux objets locaux retourné. Scott mentionne dans l'article 25, de ce quistd::move
est implicitement appliqué lorsque le compilateur choisir de ne pas éluder la copie et le programmeur ne doit pas explicitement de le faire.Dans votre cas, le code est clairement un candidat pour RVO car il renvoie l'objet local
p
et le type dep
est le même que le type de retour, ce qui entraîne copie élision. Et si le compilateur choisit de ne pas éluder la copie, pour quelque raison que ce soit,std::move
aurait botté en ligne1
.Une chose que je n'ai pas vu dans d'autres réponses,Pour clarifier un autre réponses qu'il existe une différence entre le retour std::unique_ptr qui a été créé à l'intérieur d'une fonction, et celui qui a été donné à cette fonction.L'exemple pourrait être comme ceci: