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

时间:2015-12-01 18:17:32

标签: c++ templates c++11 lambda function-pointers

考虑以下示例:

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的计算开销很大,我承担不起。即使没有这个实际的原因,我也想知道如何解决这个问题&#34;学术&#34;没有使用std::function的问题。

2 个答案:

答案 0 :(得分:2)

黑客解决方案是依赖于非捕获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,它将指向调用的指针存储在类本身中,而不是存储在vtable中。

或者,也是UB,但比上面更好 - 将无状态lambda转换为void(T*),然后将其重新解释为[{1}}。虽然仍然是UB,但大多数实现使用二进制兼容的指针大小,以及可以在其间进行转换的函数指针。 @Pradhan在评论中提出了这一点。

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

答案 1 :(得分:0)

无法将捕获lambda转换为指向函数的指针。如果你考虑它,它是相当清楚的 - 捕获你需要它们以某种方式传递给函数的变量 - 并且函数指针没有那个选项。

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