c++ - 可以采用 lambda 或函数指针并推断参数以传递给另一个模板的模板函数

标签 c++ templates c++11 lambda variadic-templates

我维护一个开源无锁线程库,专为高速并行循环展开而设计,用于几个商业视频游戏。它的开销非常低,大约 8 个时钟用于创建一条消息,大约 500 个时钟(每个线程,包括延迟)用于整个调度和远程执行开销。我首先说这个是为了解释为什么我不简单地使用 use std::function 和 bind。

该库将函数调用和函数调用的参数打包在一条消息(类型为 Functor<>)中。然后使用参数的拷贝远程调用它。

我最近重写了库以使用 STL 样式模板元编程而不是它最初使用的过时的 C 样式宏来打包远程调用。令人惊讶的是,这只增加了两个滴答的开销,但我无法弄清楚如何创建一个同时采用 lambda 和函数指针的打包函数。

预期用途:

CreateFunctor([](int a, int b){doSomething(a, b)}, 1, 2);

CreateFunctor(&doSomething, 1, 2);

目前,我必须将这些情况分为两个单独的函数(CreateFunctor 和 CreateFunctorLambda)。如果我能将它们结合起来,我就能将我的打包和调度阶段合并到一个简洁的函数调用中并简化 API。

问题在于推导 lambda 参数的代码似乎无法与推导函数参数的代码共享模板覆盖。我试过使用 enable_if 并且它仍然执行 lambda 版本的::* 部分并导致函数指针编译器错误。

相关片段:

template <typename... Arguments>
inline Functor<Arguments...> CreateFunctor(void(*func)(Arguments...))
{
    return Functor<Arguments...>(func);
};

template <typename... Arguments>
inline Functor<Arguments...> CreateFunctor(void(*func)(Arguments...), Arguments ... arg)
{
    Functor<Arguments...> ret(func);
    ret.Set(arg...);
    return ret;
};

// template to grab function type that lambda can be cast to
// from http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda
template <class T>
struct deduce_lambda_arguments
    : public deduce_lambda_arguments<typename std::enable_if<std::is_class<T>::value, decltype(&T::operator())>::type>
{};

template <class ClassType, typename... Args>
struct deduce_lambda_arguments<void(ClassType::*)(Args...) const>
    // we specialize for pointers to member function
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};

template <typename F, typename... Args>
inline auto CreateFunctorLambda(F f, Args... arg) -> typename deduce_lambda_arguments<F>::functor_type
{
    deduce_lambda_arguments<F>::functor_type ret((deduce_lambda_arguments<F>::pointer_cast_type) f);
    ret.Set(arg...);
    return ret;
};

template <typename F>
inline auto CreateFunctorLambda(F f) -> typename deduce_lambda_arguments<F>::functor_type
{
    return deduce_lambda_arguments<F>::functor_type((deduce_lambda_arguments<F>::pointer_cast_type) f);
};

最佳答案

诀窍是确保您永远不会为您想要支持但不是 lambda 的事物评估 &T::operator()。一种方法是添加一个额外的模板参数并专注于此:

template <class T, bool = std::is_class<T>::value>
struct compute_functor_type
    : public compute_functor_type<decltype(&T::operator())>
{};

template <class ClassType, typename... Args>
struct compute_functor_type<void(ClassType::*)(Args...) const, false>
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};

template <class ClassType, typename... Args>
struct compute_functor_type<void(ClassType::*)(Args...), false>
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};

template <typename... Args>
struct compute_functor_type<void(*)(Args...), false>
{
    typedef void(*pointer_cast_type)(Args...);
    typedef Functor<Args...> functor_type;
};


template <typename F, typename... Args>
inline auto CreateFunctor(F f, Args... arg) 
         -> typename compute_functor_type<F>::functor_type
{
    typename compute_functor_type<F>::functor_type ret((typename compute_functor_type<F>::pointer_cast_type) f);
    ret.Set(arg...);
    return ret;
};

template <typename F>
inline auto CreateFunctor(F f) -> typename compute_functor_type<F>::functor_type
{
    return typename compute_functor_type<F>::functor_type((typename compute_functor_type<F>::pointer_cast_type) f);
};

在这里为不必使用 MSVC 的人保存(更短的)原始方法:

因为您只关心无捕获的非泛型 lambda 并将它们转换为函数指针,所以这很容易。

从函数指针创建匹配的 Functor 类型:

template<class... Args>
Functor<Args...> make_functor(void (*f)(Args...)) { return {f}; }

并通过一元 + 运算符强制转换为函数指针:

template <class F>
inline auto CreateFunctor(F f) -> decltype(make_functor(+f))
{
    return make_functor(+f);
}

template <class F, typename... Arguments>
inline auto CreateFunctor(F f, Arguments ... arg) -> decltype(make_functor(+f))
{
    auto ret = make_functor(+f);
    ret.Set(arg...);
    return ret;
}

关于c++ - 可以采用 lambda 或函数指针并推断参数以传递给另一个模板的模板函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34185923/

相关文章:

android - 如何为 Android Studio 启用 C++11?

c++ - .NET 和 Linux 之间的 GUID/UUID 兼容性问题

android - 我可以在 Android 上安装 Node.js 吗?

c++ - 使用 enable_if 为有符号和无符号变量创建可变参数构造函数

python - 从 Django 模板访问 JSON 属性

c++ - regex_match 找不到方括号

c++ - SFINAE + decltype 的 g++ 4.7 错误?

c++ - 构造函数中变量 init 的 Astyle C++ 缩进

c++ - 如何生成 24 个字符的 UUID?

c++ - 模板化类特化,其中模板参数被模板化 : difference Visual Studio vs. g++