Перемещение лямбда: как только вы переместили захваченный тип только для перемещения, как можно использовать лямбда?
Этот ответ объясняет, как перемещать-захватывать переменную в лямбда в С++ 14.
Но как только вы перенесли захваченный объект (например, std::unique_ptr
), который не скопирован в лямбда, вы не можете скопировать сам лямбда.
Это было бы хорошо, если бы вы могли перемещать лямбда, но при попытке сделать это я получаю ошибку компиляции:
using namespace std;
class HasCallback
{
public:
void setCallback(std::function<void(void)>&& f)
{
callback = move(f);
}
std::function<void(void)> callback;
};
int main()
{
auto uniq = make_unique<std::string>("Blah blah blah");
HasCallback hc;
hc.setCallback(
[uniq = move(uniq)](void)
{
std::cout << *uniq << std::endl;
});
hc.callback();
}
Это вызывает следующую ошибку с g++
(я попытался скопировать только соответствующую строку):
error: use of deleted function ‘main()::<lambda()>::<lambda>(const main()::<lambda()>&’
... подразумевая, я думаю, что моя попытка переместить лямбда не удалась.
clang++
дает аналогичную ошибку.
Я пробовал явно move
включить лямбда (хотя это временное значение), но это не помогло.
EDIT: В нижеприведенных ответах адекватно рассматриваются ошибки компиляции, создаваемые приведенным выше кодом. Для альтернативного подхода просто release
уникальное целевое значение указателя в std::shared_ptr
, которое можно скопировать. (Я не пишу это как ответ, потому что это предполагает, что это проблема XY, но основная причина, по которой unique_ptr
не может использоваться в лямбда, которая преобразуется в std::function
, важна для понимания.)
РЕДАКТИРОВАТЬ 2: Как ни странно, я просто понял, что auto_ptr
будет делать правильные вещи здесь (!), насколько я могу судить. Он действует, по существу, как unique_ptr
, но позволяет создавать копии вместо конструкции перемещения.
Ответы
Ответ 1
Вы можете переместить лямбда, это прекрасно. Это не ваша проблема, но вы пытаетесь создать экземпляр std::function
с помощью некрупной лямбды. И:
template< class F >
function( F f );
конструктор function
делает:
5) Инициализирует цель с помощью копии f
.
Это связано с тем, что std::function
:
удовлетворяет требованиям CopyConstructible и CopyAssignable.
Так как function
должен быть скопирован, все, что вы вкладываете в него, также должно быть скопируемым. И лямбда только для движения не отвечает этому требованию.
Ответ 2
std::function
не лямбда! Это оболочка, которая может быть построена из любого типа вызываемых, включая лямбда. std::function
требует, чтобы мог быть выполнен с возможностью копирования, поэтому ваш пример не работает.
Перемещение только лямбда может быть перемещено снова, как показано ниже.
template<typename F>
void call(F&& f)
{
auto f1 = std::forward<F>(f); // construct a local copy
f1();
}
int main()
{
auto uniq = make_unique<std::string>("Blah blah blah");
auto lambda = [uniq = move(uniq)]() {
std::cout << *uniq << std::endl;
};
// call(lambda); // doesn't compile because the lambda cannot be copied
call(std::move(lambda));
}
Живая демонстрация