J'ai essayé ce qui suit:
std::function<void ()> getAction(std::unique_ptr<MyClass> &&psomething){
//The caller given ownership of psomething
return [psomething](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
Mais il ne compile pas. Des idées?
METTRE À JOUR:
Comme suggéré, une nouvelle syntaxe est nécessaire pour spécifier explicitement que nous devons transférer la propriété au lambda, je pense maintenant à la syntaxe suivante:
std::function<void ()> getAction(std::unique_ptr<MyClass> psomething){
//The caller given ownership of psomething
return [auto psomething=move(psomething)](){
psomething->do_some_thing();
//psomething is expected to be released after this point
};
}
Serait-ce un bon candidat?
MISE À JOUR 1:
Je vais montrer mon implémentation de move
et copy
comme suit:
template<typename T>
T copy(const T &t) {
return t;
}
//process lvalue references
template<typename T>
T move(T &t) {
return std::move(t);
}
class A{/*...*/};
void test(A &&a);
int main(int, char **){
A a;
test(copy(a)); //OK, copied
test(move(a)); //OK, moved
test(A()); //OK, temporary object
test(copy(A())); //OK, copying temporary object
//You can disable this behavior by letting copy accepts T &
//test(move(A())); You should never move a temporary object
//It is not good to have a rvalue version of move.
//test(a); forbidden, you have to say weather you want to copy or move
//from a lvalue reference.
}
Ce problème est résolu par capture généralisée lambda en C++ 14:
// a unique_ptr is move-only
auto u = make_unique<some_type>(some, parameters);
// move the unique_ptr into the lambda
go.run([u = move(u)]{do_something_with(u);});
Vous ne pouvez pas capturer définitivement un unique_ptr
dans une lambda. En effet, si vous voulez capturer quoi que ce soit de façon permanente dans un lambda, cela doit être copiable; simplement mobile est insuffisant.
Cela pourrait être considéré comme un défaut dans C++ 11, mais vous auriez besoin d'une syntaxe pour dire explicitement que vous vouliez déplacer le unique_ptr
valeur dans le lambda. La spécification C++ 11 est formulée très soigneusement pour empêcher les mouvements implicites sur les variables nommées; c'est pourquoi std::move
existe, et c'est une chose bonne.
Pour faire ce que vous voulez, vous devrez utiliser std::bind
(qui serait semi-alambiqué, nécessitant une courte séquence de binds
) ou renvoyant simplement un ancien objet normal.
Aussi, ne prenez jamais unique_ptr
par &&
, sauf si vous écrivez réellement son constructeur de déplacement. Prenez-le simplement par valeur; la seule façon dont un utilisateur peut la fournir par valeur est avec un std::move
. En effet, c'est généralement une bonne idée de ne jamais rien prendre par &&
, sauf si vous écrivez le constructeur de déplacement/l'opérateur d'affectation (ou implémentez une fonction de transfert).
La solution "semi-alambiquée" utilisant std::bind
comme l'a mentionné Nicol Bolas n'est pas si mal après tout:
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
std::move(psomething));
}
Une solution sous-optimale qui a fonctionné pour moi était de convertir le unique_ptr
à un shared_ptr
puis capturez le shared_ptr
dans la lambda.
std::function<void()> getAction(std::unique_ptr<MyClass> psomething)
{
//The caller given ownership of psomething
std::shared_ptr<MyClass> psomethingShared = std::shared_ptr<MyClass>(std::move(psomething));
return [psomethingShared]()
{
psomethingShared->do_some_thing();
};
}
J'ai utilisé cette solution de contournement vraiment douteuse, qui implique de coller le unique_ptr
À l'intérieur d'un shared_ptr
. En effet, mon code nécessitait un unique_ptr
(En raison d'une restriction API), je ne pouvais donc pas le convertir en un shared_ptr
(Sinon je ne pourrais jamais obtenir mon unique_ptr
Retour).
Ma justification pour utiliser cette abomination est que c'était pour mon code de test, et j'ai dû std::bind
Un unique_ptr
Dans l'appel de fonction de test.
// Put unique_ptr inside a shared_ptr
auto sh = std::make_shared<std::unique_ptr<Type>>(std::move(unique));
std::function<void()> fnTest = std::bind([this, sh, input, output]() {
// Move unique_ptr back out of shared_ptr
auto unique = std::move(*sh.get());
// Make sure unique_ptr is still valid
assert(unique);
// Move unique_ptr over to final function while calling it
this->run_test(std::move(unique), input, output);
});
Maintenant, appeler fnTest()
appellera run_test()
tout en lui passant le unique_ptr
. Appeler fnTest()
une deuxième fois entraînera un échec d'assertion, car unique_ptr
A déjà été déplacé/perdu lors du premier appel.