我需要将
unique_ptr
移至 std::function
闭包。我在 C++14 中使用广义 lambda 捕获。
auto ptr = make_unique<Foo>();
// Works.
auto lambda = [p = move(ptr)] { };
// This does not compile.
std::function<void()> func = [p = move(ptr)] { };
它试图将 lambda 捕获复制而不是移动到
std::function
中。相关错误是:
copy constructor of '' is implicitly deleted because field '' has a deleted copy
constructor
std::function<void()> func = [p = move(ptr)] { };
这里的例子会让这看起来可行。
请注意,此处的答案只是重复 isocpp.org 上的示例。
我可以移动到
shared_ptr
,如下所示:
shared_ptr<Foo> s = move(ptr);
但这会产生另一个问题,因为我需要调用一个需要 lambda 中的
unique_ptr
的函数,并且我无法将 shared_ptr
转换回 unique_ptr
。
是否可以在
unique_ptr
中捕获 std::function
?
std::function
是一个类型擦除对象,支持复制存储的对象。
当您将
std::unique_ptr
存储在 lambda 中时,该 lambda 不支持复制。
所以
std::function
的抱怨是正确的。它是一种可以复制的类型,当传入某些内容时,就会计算出如何复制它。 “我无法复制它”不是一个有效的答案;所有std::function
都可以复制。
有两种常见的方法可以解决这个问题。首先,将
std::function
的状态存储在某种 std::shared_ptr
中。其次,您编写或找到一个不可复制的 std::function
并使用它。
更“现代”
std::function
替换库支持许多有用的东西:
我个人出于各种特殊目的需要上述每一项。
然后,当您不需要复制可调用对象并且代码可以编译时,您可以使用
moveonly_function<void()>
。
但是,这对于您现在的需求来说可能太重了。
template<class F>
auto make_shared_function( F&& f ) {
return
[pf = std::make_shared<std::decay_t<F>>(std::forward<F>(f))]
(auto&&...args)->decltype(auto)
{
return (*pf)( decltype(args)(args)... );
};
}
现在每当你遇到这个问题时:
// This does not compile.
std::function<void()> func = make_shared_function([p = move(ptr)] { });
可调用对象的状态现在存储在共享 ptr 中。
从 C++23 开始,您现在可以使用 std::move_only_function 来实现您正在寻找的目标。
std::move_only_funciton
允许您将在 capute 块内移动变量的 lambda 分配给它。
$ g++ -std=c++23 -o output_file main.cpp
#include <iostream>
#include <functional>
#include <memory>
struct Foo
{
int a;
int b;
int c;
void Print()
{
std::cout << "Foo::Print is called" << std::endl;
}
};
struct Caller
{
std::move_only_function<void()> func;
};
int main()
{
auto ptr = std::make_unique<Foo>();
int a = 5;
// This will compile since C++23.
std::move_only_function<void()> func = [p = std::move(ptr), a]
{
p->Print();
std::cout << "A is " << a << std::endl;
};
Caller o;
o.func = std::move(func);
o.func();
return 0;
}