不使用 std::function 将捕获的 lambda 转换为函数指针

Casting capturing lambda to function pointer not using std::function

考虑以下示例:

using function_pair = std::pair<int, void(*)(void*)>; // This line cannot change
template <class Arg, class F>
function_pair make_function_pair(int i, F&& f)
{
    return function_pair(i, 
    [&](void* x){std::forward<F>(f)(static_cast<Arg*>(x));}); // Not working
}

// Later in code
auto p1 = make_function_pair<char>(1, [](char* x){std::cout<<*x<<std::endl;});
auto p2 = make_function_pair<double>(2, [](double* x){*x = *x*2;});
auto p3 = make_function_pair<float>(3, [](float* x){*x = *x*3;});

代码因捕获 lambda 而无法编译(当 lambda 不必捕获 f 时,它可以工作)。我想知道如何在不使用 std::function 的情况下完成这项工作,因为 std::function 的计算开销很大,我负担不起。即使没有这个实际原因,我想知道如何在不使用 std::function.

的情况下解决这个 "academic" 问题

无法将捕获的 lambda 转换为指向函数的指针。如果您考虑一下,就会很清楚 - 要捕获变量,您需要将它们以某种方式传递给函数 - 而函数指针没有该选项。

学术解决方案是重新实现std::function。没有办法解决这个问题。

hack 解决方案依赖于这样一个事实,即非捕获 lambda 不会在我见过的每个 C++ 实现中使用它的状态。

template<class F>
struct stateless_t {
  constexpr stateless_t() {
    static_assert( std::is_empty<F>::value, "Only works with stateless lambdas" );
  }
  using F_ref = F const&;
  template<class...Ts>
  std::result_of_t<F_ref(Ts...)> operator()(Ts&&...ts)const {
    return (*reinterpret_cast<F const*>(this))(std::forward<Ts>(ts)...);
  }
};

template<class F>
stateless_t<F> stateless() { return {}; }

template <class Arg, class F>
function_pair make_function_pair(int i, F const&)
{
  return function_pair(
    i, 
    [](void* x){return stateless<F>()(static_cast<Arg*>(x));}
  );
}

这是左右和中心的未定义行为,但可能适用于您的编译器。

更好的选择是使用 std::function 并在拒绝该选项之前测量开销。对于小型函数对象,std::function 的开销很小(虚拟调用而不是函数指针调用)。

接下来,编写您自己的 std::function 以减少开销或找到一个(例如最快的代表)。例如,您可以制作一个更快的 std::function 将调用指针存储在 class 本身而不是 vtable 中。

或者,也是 UB,但比上面的更好——将无状态 lambda 转换为 void(T*),然后 reinterpret_cast 转换为 void(void*)。虽然仍然是 UB,但大多数实现使用二进制兼容的指针大小,以及可以在两者之间转换的函数指针。 @Pradhan 在上面的评论中提出了这个建议。

一般来说,避免使用 UB,因为从那时起它会增加持续的维护开销。您已经在当前的编译器中测试了它,但是您必须检查它在 每个编译器 每个构建 每个编译器中是否工作设置使用 从现在到代码生命周期结束,以使其长期可靠。