C ++如何在模板中推断出Callable的类型(参数列表和返回值)

时间:2017-11-16 10:22:12

标签: c++ templates functional-programming variadic-templates variadic-functions

基本上我想要做的是创建一个函数模板,它接受任何Callable(函数类型/ lambda / Functor)并返回一个lambda-taking-the-similar-args-list并返回原始类型的返回型

#include <iostream>

int func(int a,float b) {
    return a+b;
}

struct callable {
    int operator() (int a, float b) {
        return a+b;
    }
};

template <typename RV, typename... Args>
auto getLambdaFromCallable(RV(&func)(Args...)) {
    auto l = [&](Args... args) -> RV {
        return func(args...);
    };

    return l;
}

int main() {
    auto f = getLambdaFromCallable(func);
    std::cout << f(1,2.f);
    std::cout << " " << typeid(f).name();

    auto f2 = getLambdaFromCallable(callable{}); // doesn't work
    callable{}(1,2); // works

    auto lambdaTest = [](int a, float b) -> int {
        return a+b;
    };
    auto f3 = getLambdaFromCallable(lambdaTest);
}

2 个答案:

答案 0 :(得分:6)

您可以将getLambdaFromCallable更改为:

template <typename F>
auto getLambdaFromFunction(const F& func) {
  auto l = [&](auto&&... args) 
    -> decltype(func(std::forward<decltype(args)>(args)...)) {
    return func(std::forward<decltype(args)>(args)...);
  };

  return l;
}

这背后的原因是,由于你无法获得一个详尽的参数列表,你可以调用一个函数对象(首先可能有多个重载),你也可以使用一个接受所有东西并转发的泛型lambda它可以调用。

详细说明其工作原理:

  • auto&&...部分将转换为lambda呼叫运营商的模板参数列表。

  • F被推断为你所谓的getLambdaFromFunction(没有const和引用,但如果需要可以更改)。

  • decltype(args)就是使用std::forward,而template <typename F> class generatedLambda { public: template <typename... Args> auto operator()(Args&&... args) -> decltype(func(std::forward<decltype(args)>(args)...)) { return func(std::forward<decltype(args)>(args)...); } private: F func; }; 又可以正确转发左值和左值参考,有关详细信息,请参阅std::forward

    < / LI>

生成的lambda对象如下所示:

{{1}}

答案 1 :(得分:2)

如果您可以使用C ++ 17,std::invoke是正确的工具:

#include <functional>

template <typename OBJ>
auto getLambdaFromCallable(OBJ&& obj)
{
  return [&](const auto&... args) { 
                return std::invoke(std::forward<OBJ>(obj), args...); 
         };
}

您可以使用g++ -std=c++17clang++-5.0 -std=c++1z进行编译,这是开箱即用的。