Ответ 1
Эта проблема решается обобщенный захват лямбда в С++ 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);});
Я пробовал следующее:
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
};
}
Но он не компилируется. Любые идеи?
UPDATE:
Предполагается, что для явного указания требуется какой-то новый синтаксис, нам нужно передать право собственности на лямбда, теперь я думаю о следующем синтаксисе:
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
};
}
Будет ли это хорошим кандидатом?
ОБНОВЛЕНИЕ 1:
Я покажу свою реализацию move
и copy
следующим образом:
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.
}
Эта проблема решается обобщенный захват лямбда в С++ 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);});
Вы не можете навсегда зафиксировать unique_ptr
в лямбда. Действительно, если вы хотите навсегда зафиксировать что-либо в лямбда, он должен быть скопирован; просто движимое недостаточно.
Это можно считать дефектом в С++ 11, но вам понадобится некоторый синтаксис, чтобы явно сказать, что вы хотите переместить значение unique_ptr
в лямбда. Спецификация С++ 11 очень тщательно сформулирована для предотвращения неявных движений по именованным переменным; поэтому std::move
существует, и это хорошо.
Для выполнения того, что вы хотите, потребуется либо использование std::bind
(которое было бы полупрокрутованным, требующим короткой последовательности binds
), либо просто возвращающим обычный старый объект.
Кроме того, никогда не принимайте unique_ptr
на &&
, если вы на самом деле не пишете его конструктор перемещения. Просто возьмите его по достоинству; единственный способ, которым пользователь может предоставить его по значению, - это std::move
. В самом деле, вообще хорошая идея никогда не принимать что-либо &&
, если вы не пишете оператор конструктора/назначения перемещения (или не выполняете функцию пересылки).
"Полу-запутанное" решение с использованием std::bind
, как упоминалось Николо Боласом, не так уж и плохо:
std::function<void ()> getAction(std::unique_ptr<MyClass>&& psomething)
{
return std::bind([] (std::unique_ptr<MyClass>& p) { p->do_some_thing(); },
std::move(psomething));
}
Субоптимальное решение, которое сработало для меня, состояло в том, чтобы преобразовать unique_ptr
в shared_ptr
, а затем захватить shared_ptr
в 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();
};
}
Я использовал это очень изворотливое обходное решение, которое подразумевает использование unique_ptr
внутри shared_ptr
. Это связано с тем, что для моего кода требовался unique_ptr
(из-за ограничения API), поэтому я не мог преобразовать его в shared_ptr
(иначе я никогда не смог бы вернуть мой unique_ptr
).
Мое оправдание использования этой мерзости заключается в том, что это было для моего тестового кода, и мне пришлось std::bind
a unique_ptr
вызвать вызов функции функции.
// 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);
});
Теперь вызов fnTest()
вызывает run_test()
, передавая ему unique_ptr
. Вызов fnTest()
второй раз приведет к ошибке утверждения, поскольку unique_ptr
уже был перемещен/потерян во время первого вызова.