是否有可能找出lambda的参数类型和返回类型?

时间:2011-10-30 05:47:17

标签: c++ lambda metaprogramming c++11 traits

给定一个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对象(如上所示)。

3 个答案:

答案 0 :(得分:151)

有趣的是,我刚刚根据 function_traits implementation 编写了Specializing a template on a lambda in C++0x,它可以提供参数类型。如该问题的答案所述,诀窍是使用lambda decltype的{​​{1}}。

operator()

请注意,此解决方案 不适用于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; } 这样的通用lambda。

答案 1 :(得分:11)

虽然我不确定这是严格的标准符合, 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模式的提升,我无法发布 实际代码,对不起。

答案 2 :(得分:6)

@KennyTMs答案中显示的专业化方法可以扩展到涵盖所有情况,包括可变和可变lambda:

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)

Demo

请注意,未针对可变参数operator()调整arity。相反,人们也可以考虑is_variadic