Eu tentei o seguinte:
std::function getAction(std::unique_ptr &&psomething){ //The caller given ownership of psomething return [psomething](){ psomething->do_some_thing(); //psomething is expected to be released after this point }; }
Mas não compila. Alguma ideia?
ATUALIZAR:
Como sugerido, alguma nova syntax é necessária para especificar explicitamente que precisamos transferir a propriedade para o lambda, agora estou pensando sobre a seguinte syntax:
std::function getAction(std::unique_ptr 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 }; }
Seria um bom candidato?
ATUALIZAÇÃO 1:
Vou mostrar minha implementação de move
e copy
seguinte forma:
template T copy(const T &t) { return t; } //process lvalue references template 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. }
Esse problema é tratado pela captura generalizada lambda no C ++ 14:
// a unique_ptr is move-only auto u = make_unique(some, parameters); // move the unique_ptr into the lambda go.run([u = move(u)]{do_something_with(u);});
Você não pode capturar permanentemente um unique_ptr
em um lambda. De fato, se você quiser capturar permanentemente qualquer coisa em um lambda, ele deve ser copiável ; meramente móvel é insuficiente.
Isso pode ser considerado um defeito no C ++ 11, mas você precisaria de alguma syntax para dizer explicitamente que você queria mover o valor unique_ptr
para o lambda. A especificação C ++ 11 é cuidadosamente redigida para evitar movimentos implícitos em variables nomeadas; é por isso que o std::move
existe e isso é bom .
Para fazer o que você quer, será necessário usar std::bind
(que seria semi-complicado, exigindo uma pequena seqüência de binds
) ou apenas retornar um object antigo regular.
Além disso, nunca pegue unique_ptr
by &&
, a menos que você esteja realmente escrevendo seu construtor de movimento. Apenas pegue por valor; a única maneira que um usuário pode fornecer por valor é com std::move
. Na verdade, geralmente é uma boa ideia nunca aceitar nada do &&
, a menos que você esteja escrevendo o operador de construção / atribuição de movimento (ou implementando uma function de encaminhamento).
A solução “semi-complicada” usando std::bind
como mencionado por Nicol Bolas não é tão ruim assim:
std::function getAction(std::unique_ptr&& psomething) { return std::bind([] (std::unique_ptr & p) { p->do_some_thing(); }, std::move(psomething)); }
Uma solução sub-ótima que funcionou para mim foi converter o unique_ptr
em shared_ptr
e capturar o shared_ptr
no lambda.
std::function getAction(std::unique_ptr psomething) { //The caller given ownership of psomething std::shared_ptr psomethingShared = std::shared_ptr (std::move(psomething)); return [psomethingShared]() { psomethingShared->do_some_thing(); }; }
Eu usei essa solução realmente desonesta, que envolve colocar o unique_ptr
dentro de um shared_ptr
. Isso ocorre porque meu código requeria um unique_ptr
(devido a uma restrição de API), portanto, eu não poderia convertê-lo para um shared_ptr
(senão nunca conseguiria recuperar meu unique_ptr
).
Minha justificativa para usar essa abominação é que era para o meu código de teste, e eu tinha que std::bind
um unique_ptr
na chamada de function de teste.
// Put unique_ptr inside a shared_ptr auto sh = std::make_shared>(std::move(unique)); std::function 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); });
Agora, chamar fnTest()
chamará run_test()
passando o unique_ptr
para ele. Chamar fnTest()
uma segunda vez resultará em uma falha de declaração, porque o unique_ptr
já foi movido / perdido durante a primeira chamada.