将lambda作为模板参数传递:实际推断出哪种类型?

时间:2019-04-04 17:26:20

标签: c++ lambda c++14 std-function template-deduction

如果我将lambda作为模板参数传递,则推论该参数的实际类型是什么?我查看了VS2017调试器,该lambda的类型为:[](int x) {return x; }filename::__I2::int<lambda>(int)

之所以这样问,是因为我想传递一个lambda,然后从中创建一个内部std::function。请注意,这与this answer有关,为什么我们必须使用CTAD构造内部std::function而不是仅将模板参数传递给std::function

作为一个例子,我想做如下事情:

template<class Func, class... Args> 
void createStdFunc(Func f, Args... args) {
    std::function<Func> internalFunc = f; //this does not work
}

//usage
createStdFunc([](int x) {return x; }, 5);

但是,这不起作用,并且出现错误'initialising' cannot convert from 'Func' to 'std::function<Func>'。我不确定类型从传递到函数到初始化std::function的不同之处以及它们的变化。请注意,我确实知道您可以从2017年开始使用CTAD,但想知道2014年及以前的解决方案是什么?

5 个答案:

答案 0 :(得分:3)

在C ++ 14中,您可以使用返回类型推导找出函数签名,这意味着传递给createStdFunc的参数类型匹配:

template<class Func, class... Args> 
void createStdFunc(Func f, Args... args) {
    std::function<std::result_of_t<Func(Args...)> (Args...)> internalFunc{f}; //this does work
}

答案 1 :(得分:2)

我的方式

2em

也许也可以通过#include <iostream> #include <functional> template <typename R, typename T, typename ... As> constexpr std::function<R(As...)> getFuncType (R(T::*)(As...) const); template <typename F, typename ... As> void createStdFunc (F const & f, As ... as) { decltype(getFuncType(&F::operator())) internalFunc { f }; internalFunc(as...); } int main () { createStdFunc([](int x) { std::cout << x << std::endl; }, 5); }

using

答案 2 :(得分:1)

代码内部的问题是Func不是函数类型。这是lambda的类型。 Lambda编译成这样的东西:

// equivalent:
// auto my_lambda = [](int v){ return v; };

struct /* unnamed */ {
    auto operator()(int v) const { return v; }
} my_lambda;

解决方案是从闭包类型中提取operator()的类型:

using my_lambda_t = decltype(my_lambda);

// type: int(my_lambda_t::*)(int) const; 
auto call_operator = &decltype(my_lambda_t)::operator();

然后,从operator()的类型中,可以推断出参数的类型和返回类型:

template<typename>
struct extract_types {};

template<typename R, typename C, typename... Args>
struct extract_types<R(C::*)(Args...) const> {
    using result = R;
    using args_types = std::tuple<Args...>;
};

此模式的通用版本在Boost.CallableTraits中提供

答案 3 :(得分:1)

您可以编写一个简单的特征来概括可调用类型。如果使用operator()和{{1}和非const都可以处理函数指针和任何东西,那么您应该可以涵盖大多数用例。

const
然后,

#include <tuple> // For callable types template<class T> struct func_type : func_type<decltype(&T::operator())>{}; // For callable types' member functions (including `operator()`) template<class T, class R, class ... Args > struct func_type<R (T::*)(Args...) const> : func_type<R(*)(Args...)> {}; // For function pointers template<class R, class ... Args > struct func_type<R (*)(Args...)> { using type = R(Args...); using result = R; using args = std::tuple<Args...>; }; template<class T> using func_type_t = typename func_type<T>::type; 应该为您提供大多数可调用类型func_type_t<T>的功能类型。示例使用:

T

答案 4 :(得分:1)

std::function模板期望将函数类型作为其自变量,从中可以推断出可调用包装的返回和参数类型。 lambda表达式的闭包类型是可调用的,但不是函数类型。

C ++ 17为std::function引入了deduction guides,它允许从任何可调用的参数推导出正确的类型。在C ++ 17之前的版本中,您可以使用一组帮助程序模板来推断正确的类型,例如:

template <typename F>
struct deduce_func_type_helper;

template <typename R, typename... Args>
struct deduce_func_type_helper<R(&)(Args...)>
{
    using type = std::function<R(Args...)>;
};

template <typename R, typename... Args>
struct deduce_func_type_helper<R(*)(Args...)> : deduce_func_type_helper<R(&)(Args...)> {};

template <typename C, typename R, typename... Args>
struct deduce_func_type_helper<R(C::*)(Args...)> : deduce_func_type_helper<R(&)(Args...)> {};

template <typename C, typename R, typename... Args>
struct deduce_func_type_helper<R(C::*)(Args...) const> : deduce_func_type_helper<R(&)(Args...)> {};

template <typename C, typename R, typename... Args>
struct deduce_func_type_helper<R(C::*)(Args...) volatile> : deduce_func_type_helper<R(&)(Args...)> {};

template <typename F>
struct deduce_func_type_helper<F&> : deduce_func_type_helper<std::remove_cv_t<F>> {};

template <typename F>
struct deduce_func_type_helper<F&&> : deduce_func_type_helper<std::remove_cv_t<F>> {};

template <typename F>
struct deduce_func_type_helper : deduce_func_type_helper<decltype(&F::operator())> {};

template <typename F>
using func_type_t = typename deduce_func_type_helper<F>::type;

live example here

请注意,上面的示例还不完整;它缺少一些专门知识,例如,constvolatile和所有不同的ref限定词的所有可能组合。因此,这可能会很冗长,如果可以的话,您可能希望使用C ++ 17。