是否可以计算出一个 lambda 的参数类型和返回类型?

给定一个 lambda,是否可以计算出它的参数类型和返回类型? 如果是,如何计算?

基本上,我希望 lambda_traits可以用于以下几个方面:

auto lambda = [](int i) { return long(i*10); };


lambda_traits<decltype(lambda)>::param_type  i; //i should be int
lambda_traits<decltype(lambda)>::return_type l; //l should be long

其背后的动机是我想在一个接受 lambda 作为参数的函数模板中使用 lambda_traits,我需要知道它的参数类型和函数内部的返回类型:

template<typename TLambda>
void f(TLambda lambda)
{
typedef typename lambda_traits<TLambda>::param_type  P;
typedef typename lambda_traits<TLambda>::return_type R;


std::function<R(P)> fun = lambda; //I want to do this!
//...
}

目前,我们可以假设 lambda 只有一个参数。

最初,我试图与 std::function合作:

template<typename T>
A<T> f(std::function<bool(T)> fun)
{
return A<T>(fun);
}


f([](int){return true;}); //error

但显然会出错。因此,我将其更改为函数模板的 TLambda版本,并希望在函数内部构造 std::function对象(如上所示)。

33647 次浏览

虽然我不确定这是否符合标准, Ideone 编译了以下代码:

template< class > struct mem_type;


template< class C, class T > struct mem_type< T C::* > {
typedef T type;
};


template< class T > struct lambda_func_type {
typedef typename mem_type< decltype( &T::operator() ) >::type type;
};


int main() {
auto l = [](int i) { return long(i); };
typedef lambda_func_type< decltype(l) >::type T;
static_assert( std::is_same< T, long( int )const >::value, "" );
}

但是,这只提供了函数类型,那么结果和参数呢 类型必须从中提取。 如果你能使用 boost::function_traitsresult_typearg1_type 就能达到目的。 由于 ideone 似乎不能在 C + + 11模式下提供增强,我不能发表文章 真正的密码,抱歉。

有趣的是,我刚刚写了一个基于 在 C + + 0x 中专门化一个 lambda 上的模板function_traits实现,它可以给出参数类型。正如这个问题的答案所描述的,诀窍在于使用 lambda 的 operator()decltype

template <typename T>
struct function_traits
: public function_traits<decltype(&T::operator())>
{};
// For generic types, directly use the result of the signature of its 'operator()'


template <typename ClassType, typename ReturnType, typename... Args>
struct function_traits<ReturnType(ClassType::*)(Args...) const>
// we specialize for pointers to member function
{
enum { arity = sizeof...(Args) };
// arity is the number of arguments.


typedef ReturnType result_type;


template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
// the i-th argument is equivalent to the i-th tuple element of a tuple
// composed of those arguments.
};
};


// test code below:
int main()
{
auto lambda = [](int i) { return long(i*10); };


typedef function_traits<decltype(lambda)> traits;


static_assert(std::is_same<long, traits::result_type>::value, "err");
static_assert(std::is_same<int, traits::arg<0>::type>::value, "err");


return 0;
}

注意,这个解决方案 没有适用于类似 [](auto x) {}的通用 lambda。

@ KennyTM 答案中显示的专门化方法可以扩展到所有情况,包括可变和可变的 lambdas:

template <typename T>
struct closure_traits : closure_traits<decltype(&T::operator())> {};


#define REM_CTOR(...) __VA_ARGS__
#define SPEC(cv, var, is_var)                                              \
template <typename C, typename R, typename... Args>                        \
struct closure_traits<R (C::*) (Args... REM_CTOR var) cv>                  \
{                                                                          \
using arity = std::integral_constant<std::size_t, sizeof...(Args) >;   \
using is_variadic = std::integral_constant<bool, is_var>;              \
using is_const    = std::is_const<int cv>;                             \
\
using result_type = R;                                                 \
\
template <std::size_t i>                                               \
using arg = typename std::tuple_element<i, std::tuple<Args...>>::type; \
};


SPEC(const, (,...), 1)
SPEC(const, (), 0)
SPEC(, (,...), 1)
SPEC(, (), 0)

演示。

请注意,基数没有调整为可变的 operator()s。相反,一个人也可以考虑 is_variadic

@ KennyTMs 提供的答案非常好用,但是如果 lambda 没有参数,使用索引 arg < 0 > 不会编译。如果其他人有这个问题,我有一个简单的解决方案(比使用 SFINAE 相关的解决方案更简单)。

只需在 arg 结构中的 tuple 的结尾添加 void,即可变参数类型之后。

template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type;
};

因为参数的多少并不依赖于模板参数的实际数量,所以实际的参数不会是不正确的,如果参数为0,那么至少 arg < 0 > 仍然存在,您可以使用它做您想做的事情。如果您已经计划不超过指数 arg<arity-1>,那么它不应该干扰您当前的实现。

如果您正在为 C + + 中可调用的所有类型寻找一个完整的解决方案,那么这些解决方案中有许多是有效的,但是缺少一些最关键的例子,比如

  • 引用一个 λ
  • 函数和函数指针

这里有一个完整的解决方案,我的知识(除了通用的 lambdas)-让我知道在评论中,如果有什么缺失:

template <typename>
struct closure_traits;


template <typename FunctionT> // overloaded operator () (e.g. std::function)
struct closure_traits
: closure_traits<decltype(&std::remove_reference_t<FunctionT>::operator())>
{
};


template <typename ReturnTypeT, typename... Args> // Free functions
struct closure_traits<ReturnTypeT(Args...)>
{
using arguments = std::tuple<Args...>;


static constexpr std::size_t arity = std::tuple_size<arguments>::value;


template <std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;


using return_type = ReturnTypeT;
};


template <typename ReturnTypeT, typename... Args> // Function pointers
struct closure_traits<ReturnTypeT (*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
};


// member functions
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
: closure_traits<ReturnTypeT(Args...)>
{
using class_type = ClassTypeT;
};


// const member functions (and lambda's operator() gets redirected here)
template <typename ReturnTypeT, typename ClassTypeT, typename... Args>
struct closure_traits<ReturnTypeT (ClassTypeT::*)(Args...) const>
: closure_traits<ReturnTypeT (ClassTypeT::*)(Args...)>
{
};


免责声明: std::remove_reference的灵感来自 这个密码