移动 lambda:一旦你移动捕获了只移动类型,如何使用 lambda? [复制]
Posted
技术标签:
【中文标题】移动 lambda:一旦你移动捕获了只移动类型,如何使用 lambda? [复制]【英文标题】:Moving a lambda: once you've move-captured a move-only type, how can the lambda be used? [duplicate] 【发布时间】:2015-12-05 20:09:45 【问题描述】:This answer 解释了如何在 C++14 中移动捕获 lambda 中的变量。
但是,一旦您在 lambda 中移动捕获了一个不可复制的对象(例如 std::unique_ptr
),您就无法复制 lambda 本身。
如果您可以移动 lambda,这会很好,但尝试这样做时会出现编译错误:
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()>&’
...我认为,这意味着我移动 lambda 的尝试失败了。
clang++
给出了类似的错误。
我尝试明确地move
ing lambda(即使它是一个临时值),但这并没有帮助。
编辑: 下面的答案充分解决了上述代码产生的编译错误。对于另一种方法,只需将唯一指针的目标值release
转换为std::shared_ptr
,可以复制。 (我不是在写这个作为答案,因为那会假设这是一个 XY 问题,但是 unique_ptr
不能在转换为 std::function
的 lambda 中使用的根本原因很重要理解.)
编辑 2: 有趣的是,据我所知,我刚刚意识到 auto_ptr
实际上会在这里做正确的事情(!)。它的行为本质上类似于unique_ptr
,但允许复制构造代替移动构造。
【问题讨论】:
我认为 setCallback 应该通过值而不是右值引用来获取参数,我错了吗? @Slava 这就是我最初拥有的,但它给出了同样的错误。我认为采用右值引用将允许(/强制)移动构造 lambda,但情况似乎并非如此。 【参考方案1】:你可以移动lambda,没关系。不过,这不是您的问题,您正在尝试使用不可复制的 lambda 实例化 std::function
。还有:
template< class F >
function( F f );
function
的构造函数:
5) 使用
f
的副本 初始化目标。
这是因为std::function
:
满足 CopyConstructible 和 CopyAssignable 的要求。
由于function
必须是可复制的,因此您放入其中的所有内容也必须是可复制的。而只能移动的 lambda 不满足该要求。
【讨论】:
....呵呵。谢谢 - 即使对于这个简单的案例,我也很难解析这些错误消息。除了使用通用引用限定模板参数而不是std::function
之外,有什么方法可以更改签名以工作?
@KyleStrand 参数是什么并不重要,你只是不能构造function
。如果您需要类型擦除的内容,则必须编写可移动的 function
等效项。
@Barry 已经有了 move only std::functions 替代品,只提两个:github.com/Naios/Function2 和 github.com/potswa/cxx_function
@Barry 我的意思是直接使用 lambda(不将其转换为 function
或任何其他类似类型),方法是编写一个将 lambda 本身用作模板类型的模板函数。
@KyleStrand 哦,当然,没关系。移动 lambda 是非常好的。【参考方案2】:
std::function
不是 lambda!它是一个可以从任何类型的可调用对象(包括 lambda)构造的包装器。 std::function
需要 callable be copy-constructible,这就是您的示例失败的原因。
一个只能移动的 lambda 可以再次移动,如下所示。
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));
Live demo
【讨论】:
确认。我知道std::function
不是 lambda,但由于 lambda 可以转换为 std::function
,所以我并没有清楚地记住这个区别。
那么如何在原代码中实现HasCallback呢?我的意思是,如何将仅移动 lambda 保存到仅移动容器或仅移动数据结构中?
@alpha 您要么需要直接使用 lambda 而不将其转换为另一种类型(即调用 lambda 的函数需要将 lambda 作为模板类型参数),要么使用不同的函数类(std::function
在野外有多种替代方案,或者您可以自己设计)。以上是关于移动 lambda:一旦你移动捕获了只移动类型,如何使用 lambda? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
将 packaged_task 对象移动到 lambda 捕获时出错
如果 lambda 使用 std::move() 捕获不可复制的对象,为啥它是不可移动的?