Comment puis-je passer un std::unique_ptr
dans une fonction? Disons que j'ai la classe suivante:
class A
{
public:
A(int val)
{
_val = val;
}
int GetVal() { return _val; }
private:
int _val;
};
Ce qui suit ne compile pas:
void MyFunc(unique_ptr<A> arg)
{
cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
MyFunc(ptr);
return 0;
}
Pourquoi ne puis-je pas passer un std::unique_ptr
dans une fonction? C’est sûrement le but principal de la construction? Ou bien le comité C++ avait-il l'intention de me rabattre sur des pointeurs bruts de style C et de le transmettre comme suit:
MyFunc(&(*ptr));
Et le plus étrange de toutes, pourquoi est-ce une bonne façon de le faire passer? Cela semble horriblement incohérent:
MyFunc(unique_ptr<A>(new A(1234)));
Il y a essentiellement deux options ici:
void MyFunc(unique_ptr<A> & arg)
{
cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
MyFunc(ptr);
}
Notez que dans ce cas, l'assertion sera vérifiée!
void MyFunc(unique_ptr<A> arg)
{
cout << arg->GetVal() << endl;
}
int main(int argc, char* argv[])
{
unique_ptr<A> ptr = unique_ptr<A>(new A(1234));
MyFunc(move(ptr));
assert(ptr == nullptr)
}
Vous le transmettez par valeur, ce qui implique de faire une copie. Ce ne serait pas très unique, n'est-ce pas?
Vous pouvez déplacer la valeur, mais cela implique de passer la propriété de l'objet et le contrôle de sa durée de vie à la fonction.
Si la durée de vie de l'objet est garantie pendant toute la durée de l'appel de MyFunc, il suffit de passer un pointeur brut via ptr.get()
.
Pourquoi ne puis-je pas passer un
unique_ptr
Dans une fonction?
Vous ne pouvez pas faire cela parce que unique_ptr
A un constructeur de déplacement mais pas un constructeur de copie. Selon la norme, lorsqu'un constructeur de déplacement est défini mais qu'un constructeur de copie n'est pas défini, le constructeur de copie est supprimé.
12.8 Copier et déplacer des objets de classe
...
7 Si la définition de classe ne déclare pas explicitement un constructeur de copie, celui-ci est déclaré implicitement. Si la définition de classe déclare un constructeur de déplacement ou un opérateur d'affectation de déplacement, le constructeur de copie implicitement déclaré est défini comme supprimé.
Vous pouvez passer le unique_ptr
À la fonction en utilisant:
void MyFunc(std::unique_ptr<A>& arg)
{
cout << arg->GetVal() << endl;
}
et utilisez-le comme vous l'avez:
ou
void MyFunc(std::unique_ptr<A> arg)
{
cout << arg->GetVal() << endl;
}
et l'utiliser comme:
std::unique_ptr<A> ptr = std::unique_ptr<A>(new A(1234));
MyFunc(std::move(ptr));
note importante
Veuillez noter que si vous utilisez la deuxième méthode, ptr
n'est pas propriétaire du pointeur après l'appel de std::move(ptr)
.
void MyFunc(std::unique_ptr<A>&& arg)
aurait le même effet que void MyFunc(std::unique_ptr<A>& arg)
puisque les deux sont des références.
Dans le premier cas, ptr
est toujours propriétaire du pointeur après l'appel à MyFunc
.
Pourquoi ne puis-je pas passer un
unique_ptr
dans une fonction?
Vous pouvez le faire, mais pas par copie, car std::unique_ptr<>
n'est pas copiable.
C’est sûrement le but principal de la construction?
Entre autres, std::unique_ptr<>
est conçu pour marquer sans équivoque nique propriété (par opposition à std::shared_ptr<>
).
Et le plus étrange de toutes, pourquoi est-ce une bonne façon de le faire passer?
Parce que dans ce cas, il n'y a pas de construction de copie.
Comme MyFunc
ne s'en approprie pas, il serait préférable d'avoir:
void MyFunc(const A* arg)
{
assert(arg != nullptr); // or throw ?
cout << arg->GetVal() << endl;
}
ou mieux
void MyFunc(const A& arg)
{
cout << arg.GetVal() << endl;
}
Si vous voulez vraiment vous approprier, vous devez déplacer votre ressource:
std::unique_ptr<A> ptr = std::make_unique<A>(1234);
MyFunc(std::move(ptr));
ou passez directement une référence de valeur r:
MyFunc(std::make_unique<A>(1234));
std::unique_ptr
n'a pas exprès de copie pour garantir d'avoir un seul propriétaire.
Puisque unique_ptr
est la propriété unique, si vous voulez passer en argument, essayez
MyFunc(move(ptr));
Mais après cela, l'état de ptr
dans main
sera nullptr
.