C++11 将函数作为 lambda 参数传递

标签 c++ c++11 gcc clang clang++

最近,我在将作为参数传递的函数传递给 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/

相关文章:

c++ - 按值返回 std::vector

c++ - 防止在返回时复制对象

c++ - 无符号值之间的减法 - 意外结果

c++ - 在 C++ header 中定义的函数中重用对象

c++ - C++ 隐式转换原语的警告或错误

c++ - 在 C++ 中获取正确编码的字符串

c++ - 转换字符串标记流时如何避免重复istringstream构造

c++ - 我如何对 condition_variable::wait 周围的包装器进行单元测试?

c++ - 只有 MSVC 能够编译这段代码

C 源代码在某些目录中包含文件(如 dir/header.h),但 header 在另一个目录中