Un unique_ptr
Ne peut pas être repoussé dans un std::vector
Car il n'est pas copiable, sauf si std::move
Est utilisé. Cependant, que F
soit une fonction qui renvoie un unique_ptr
, L'opération std::vector::Push_back(F())
est autorisée. Voici un exemple ci-dessous:
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int f() { return _f + 10; }
private:
int _f = 20;
};
std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }
int main() {
std::unique_ptr<A> p1(new A());
std::vector< std::unique_ptr<A> > v;
v.Push_back(p1); // (1) This fails, should use std::move
v.Push_back(create()); // (2) This doesn't fail, should use std::move?
return 0;
}
(2)
Est autorisé, mais (1)
Ne l'est pas. Est-ce parce que la valeur retournée est déplacée d'une manière ou d'une autre implicitement?
Dans (2)
, Est-il réellement nécessaire d'utiliser std::move
?
std::move(X)
signifie essentiellement "ici, traitez X comme s'il s'agissait d'un objet temporaire".
create()
renvoie un std::unique_ptr<A>
temporaire pour commencer, donc move
n'est pas nécessaire.
Si vous voulez en savoir plus, regardez dans catégories de valeurs . Votre compilateur utilise des catégories de valeurs pour déterminer si une expression fait référence à un objet temporaire ("rvalue") ou non ("lvalue").
p1
Est une valeur l et create()
est une valeur r.
std::vector::Push_back()
a une surcharge qui prend en entrée une référence rvalue:
void Push_back( T&& value );
La valeur de retour de create()
est une valeur temporaire sans nom, c'est-à-dire une valeur r, elle peut donc être transmise telle quelle à Push_back()
sans avoir besoin d'utiliser std::move()
dessus.
std::move()
n'est nécessaire que lors du passage d'une variable nommée, c'est-à-dire une lvalue, où une rvalue est attendue.
Avec C++ 11, nous avons obtenu des constructeurs de mouvements et une sémantique de rvalues.
std :: move (X) est juste un transtypage en une rvalue qui convertit X en X && c'est tout. Ensuite, move ctor prend le relais et les constructeurs de déplacement "volent" généralement les ressources détenues par l'argument. unique_ptr possède un ctor de déplacement.
Les valeurs de retour de fonction sont déjà une rvalue (sauf si la fonction renvoie une référence lvalue comme indiqué par @HolyBlackCat dans les commentaires) qui déclenchera le déplacement du ctor sans avoir besoin de cast supplémentaire. Et puisque move ctor est défini pour unique_ptr, il sera compilé.
Également la raison pour laquelle v.Push_back (p1); échec est: vous essayez d'appeler le constructeur de copie avec une valeur l et il échoue parce que unique_ptr n'a pas de ctor de copie.
Il convient également de savoir que cela fonctionnerait également en raison de la capacité du compilateur à déplacer des objets qui ne se déplacent pas explicitement (NRVO)
#include <iostream>
#include <vector>
#include <memory>
class A {
public:
int f() { return _f + 10; }
private:
int _f = 20;
};
std::unique_ptr<A> create() {
std::unique_ptr<A> x (new A);
return x;
}
int main() {
std::unique_ptr<A> p1(new A());
std::vector< std::unique_ptr<A> > v;
//v.Push_back(p1); // (1) This fails, should use std::move
v.Push_back(create()); // (2) This doesn't fail, should use std::move?
return 0;
}