最近,我在将作为参数传递的函数传递给 lambda 表达式时遇到了一个奇怪的问题。代码在 clang 3.5+ 上编译得很好,但在 g++ 5.3 上失败了,我想知道问题是出在 c++ 标准、clang 中的非标准扩展还是 GCC 中的无效句法解释。
示例代码非常简单:
template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type>
std::future<T>
async(Fn &&fn, Args &&... args)
{
std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>();
auto lambda = [promise, fn, args...](void)
{ promise->set_value(fn(std::move(args)...)); };
send_message(std::make_shared<post_call>(lambda));
return promise->get_future();
};
GCC 报告如下:
error: variable ‘fn’ has function type
{ promise->set_value(fn(std::move(args)...)); };
(...)
error: field ‘async(Fn&&, Args&& ...) [with Fn = int (&)(int, int); Args = {int&, int&}; T = int]::<lambda()>::<fn capture>’ invalidly declared function type
auto lambda = [promise, fn, args...](void)
幸运的是,我找到了一个简单的解决方法,添加一个std::function对象,封装函数参数:
template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type>
std::future<T>
async(Fn &&fn, Args &&... args)
{
std::shared_ptr<std::promise<T>> promise = std::make_shared<std::promise<T>>();
std::function<T(typename std::remove_reference<Args>::type...)> floc = fn;
auto lambda = [promise, floc, args...](void)
{ promise->set_value(floc(std::move(args)...)); };
send_message(std::make_shared<post_call>(lambda));
return promise->get_future();
};
虽然我不完全理解第一段代码有什么问题,但是用clang成功编译并运行没有错误。
编辑
我刚刚注意到,如果其中一个参数应该是引用,我的解决方案就会灾难性地失败。因此,如果您有任何其他可能适用于 C++11 的建议(即没有专门的 lambda 捕获 [c++14 功能]),那将非常酷...
最佳答案
变量fn
具有函数类型。特别是,它具有类型 Fn = int (&)(int, int)
。
Fn
类型的值(不是引用)是 int(int,int)
类型。
无法存储该值。
我有点惊讶它不会为你自动衰减。在 C++14 中,您可以:
auto lambda = [promise, fn=fn, args...](void)
{ promise->set_value(fn(std::move(args)...)); };
这应该将 fn
的类型(外部)衰减为内部的 fn
。如果这不起作用:
auto lambda = [promise, fn=std::decay_t<Fn>(fn), args...](void)
{ promise->set_value(fn(std::move(args)...)); };
明确地衰减它。 (衰减是一种使类型适合存储的操作)。
其次,你应该添加mutable
:
auto lambda = [promise, fn=std::decay_t<Fn>(fn), args...](void)mutable
{ promise->set_value(fn(std::move(args)...)); };
或者 std::move
不会做太多事情。 (移动 const 值并没有太大作用)。
第三,你可以移动 promise 而不是创建一个不必要的共享 ptr:
template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type>
std::future<T>
async(Fn &&fn, Args &&... args)
{
std::promise<T> promise;
std::future<T> ret = promise.get_future();
auto lambda =
[promise=std::move(promise), fn=std::decay_t<Fn>(fn), args...]
() mutable {
promise.set_value(fn(std::move(args)...));
};
send_message(std::make_shared<post_call>(lambda));
return ret;
};
这假定您的 post_call
类可以处理仅移动的 lambda(如果它是 std::function
,则不能)。
关于C++11 将函数作为 lambda 参数传递,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34815698/