将捕获 lambda 作为函数指针传递

新手上路,请多包涵

是否可以将 lambda 函数作为函数指针传递?如果是这样,我一定是做错了什么,因为我得到了一个编译错误。

考虑以下示例

using DecisionFn = bool(*)();

class Decide
{
public:
    Decide(DecisionFn dec) : _dec{dec} {}
private:
    DecisionFn _dec;
};

int main()
{
    int x = 5;
    Decide greaterThanThree{ [x](){ return x > 3; } };
    return 0;
}

当我 尝试编译这个 时,我得到以下编译错误:

 In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9:  note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5:   note: Decide::Decide(DecisionFn)
9:5:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7:   note: constexpr Decide::Decide(const Decide&)
6:7:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7:   note: constexpr Decide::Decide(Decide&&)
6:7:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'

这是一条需要消化的错误消息,但我认为我从中得到的是 lambda 不能被视为 constexpr 所以我不能将它作为函数指针传递?我也尝试过制作 x constexpr ,但这似乎没有帮助。

原文由 Cory Kramer 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.9k
2 个回答

如果 lambda 没有捕获,则只能将其转换为函数指针,从 草案 C++11 标准 部分 5.1.2 [expr.prim.lambda] 说( _强调我的_):

没有 lambda 捕获 的 lambda 表达式的闭包类型具有一个公共的非虚拟非显式 const 转换函数,该函数指向 具有与闭包类型的函数调用运算符相同的参数和返回类型的函数的指针。这个转换函数的返回值应该是一个函数的地址,当被调用时,它与调用闭包类型的函数调用运算符具有相同的效果。

请注意,cppreference 在其关于 Lambda 函数 的部分中也对此进行了介绍。

因此,以下替代方案将起作用:

 typedef bool(*DecisionFn)(int);

Decide greaterThanThree{ []( int x ){ return x > 3; } };

这也是这样:

 typedef bool(*DecisionFn)();

Decide greaterThanThree{ [](){ return true ; } };

并且正如 5gon12eder 指出的那样,您也可以使用 std::function ,但请注意 std::function 是重量级的,所以这不是一个低成本的权衡。

原文由 Shafik Yaghmour 发布,翻译遵循 CC BY-SA 3.0 许可协议

这是解决方案的另一种变体。 C++14(可转为 C++11 )支持返回值、不可复制和可变 lambda。如果不需要可变 lambda,则可以通过删除与非常量版本匹配的特化并嵌入 impl_impl 来更短。

对于那些想知道的人来说,它之所以有效,是因为每个 lambda 都是唯一的(是不同的类),因此调用 to_f 会为这个 lambda 静态和相应的可以访问它的 C 样式函数生成唯一的。

 template <class L, class R, class... Args> static auto impl_impl(L l) {
  static_assert(!std::is_same<L, std::function<R(Args...)>>::value,
                "Only lambdas are supported, it is unsafe to use "
                "std::function or other non-lambda callables");

    static L lambda_s = std::move(l);
    return +[](Args... args) -> R { return lambda_s(args...); };
}

template <class L>
struct to_f_impl : public to_f_impl<decltype(&L::operator())> {};
template <class ClassType, class R, class... Args>
struct to_f_impl<R (ClassType::*)(Args...) const> {
  template <class L> static auto impl(L l) {
    return impl_impl<L, R, Args...>(std::move(l));
  }
};
template <class ClassType, class R, class... Args>
struct to_f_impl<R (ClassType::*)(Args...)> {
  template <class L> static auto impl(L l) {
    return impl_impl<L, R, Args...>(std::move(l));
  }
};

template <class L> auto to_f(L l) { return to_f_impl<L>::impl(std::move(l)); }

请注意,这也往往适用于其他可调用对象,如 std::function 但如果它不起作用会更好,因为与 lambdas 不同,std::function 类对象不会生成唯一类型,因此内部模板及其内部静态将被所有具有相同签名的函数重用/共享,这很可能不是我们想要的。我已经明确禁止 std::function 但还有更多我不知道如何以通用方式禁止。

原文由 Lyubomyr Semkiv 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题