为lambda捕获放置新的

时间:2016-06-30 15:18:00

标签: c++ c++11 lambda

我对C ++ lambdas有一个有趣的问题。用例是一个性能关键的线程,它构造一个带有非空闭包的lambda,然后传递给另一个线程,以便它可以“工作”。

总体思路有效,但我的lambda闭包对象被复制了一次,而编译器(目前是g ++ 5.4.0)无法优化此副本,包括copy elision。我也尝试过类似的结果。

理想情况下,我想在持久内存中直接创建lambda对象时使用placement new,而不是首先在堆栈上创建对象,例如:

auto l = new (buf) [a,b,c](){}

错误类似于:

lambda_ctor.cpp:39:19: error: expected type-specifier before '[' token
    auto al = (buf) new [a,b,c](){};

我在想,如果我手中有一个类型而不是实际的lambda表达式编译器应该接受它但是试图解除闭包类型而不进行评估会遇到另一个问题:

using T = decltype([a,b,c](){});

出现此错误:

lambda_ctor.cpp:41:24: error: lambda-expression in unevaluated context
    using T = decltype([a,b,c](){});

我似乎无法找到解决这个问题的方法,有很多关于如何防止lambda在范围退出时崩溃的想法,但每个解决方案似乎都涉及复制/移动闭包对象一旦创建(又名)额外的副本)。这有点奇怪,因为通常我们可以完全控制如何创建和传递用户定义的仿函数,并且类似的规则应该应用于闭包。

2 个答案:

答案 0 :(得分:2)

auto是你的朋友。

auto l = new (buf) auto([a,b,c](){});

闭包对象的任何副本都应该是elidable。

答案 1 :(得分:1)

您可以执行类似

的操作
auto l = new (buf) auto([a, b, c]{});

但你必须知道buf有足够的空间来容纳lambda。为了找到实际lambda的大小,你需要做类似的事情

auto l = [a, b, c]{};
auto size = sizeof(l);

但是现在你已经在堆栈上创建了lambda,你不想这样做。但现在你知道缓冲区有多大,并且可以做到

auto buf = new char[size];
auto lptr = new (buf) auto(std::move(l));

lambda将被移入缓冲区。

但是,最终,你需要破坏lambda并释放缓冲区。使用模板函数可以轻松破坏lambda。

template<typename Lambda>
void destruct(Lambda* l)
{
    l->~Lambda();
}