最近,我将一个作为参数传递的函数传递给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();
};
海湾合作委员会报告了以下内容:
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功能]),那真的很酷......
答案 0 :(得分:7)
变量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值并没有太大作用)。
第三,你可以转移承诺,而不是创建一个不必要的共享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
它不能处理它。)
答案 1 :(得分:0)
您的第一个代码在VS2015下编译得很好,所以我无法重现您的问题,但由于您使用的是通用引用,我会尝试类似:
template<typename Fn, typename... Args, typename T = typename std::result_of<Fn(Args...)>::type>
std::future<T> async2(Fn &&fn, Args &&... args)
{
std::promise<T> prom;
auto floc = std::bind(std::forward<Fn>(fn), std::forward<Args>(args)...);
auto lambda = [&prom, floc](void)
{ prom.set_value(floc()); };
send_message(std::make_shared<post_call>(lambda));
return prom.get_future();
};