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


135

给定一个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_traits在接受lambda作为参数的函数模板中使用,并且我需要知道函数内部的参数类型和返回类型:

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内部对象(如上所示)。


如果你知道参数类型,那么可以用来找出返回类型。我不知道如何找出参数类型。
Mankarse 2011年

是否假定函数采用单个参数?
iammilind

1
“参数类型”,但是任意的lambda函数没有参数类型。它可以采用任意数量的参数。因此,必须将任何特征类都设计为通过位置索引查询参数。
Nicol Bolas's

@iammilind:是的。目前,我们可以假设这一点。
纳瓦兹

@NicolBolas:目前,我们可以假设lambda恰好接受一个参数。
纳瓦兹

Answers:


160

有趣的是,我刚刚基于C ++ 0x中的lambda专门化模板编写了一个function_traits实现,该实现可以提供参数类型。如该问题答案中所述,技巧是使用lambda的。 decltypeoperator()

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这样的[](auto x) {}


呵呵,我只是在写这个。没想到tuple_element,谢谢。
GManNickG 2011年

@GMan:如果您的方法与此不完全相同,请发布。我将测试此解决方案。
纳瓦兹

3
const对于声明为mutable[]() mutable -> T { ... })的lambda,完整特征也将对non-使用特殊化。
卢克·丹顿

1
@Andry是具有(可能)多次重载而operator()不是通过此实现实现的功能对象的基本问题。auto不是一种类型,因此永远不可能成为答案traits::template arg<0>::type
Caleth

1
@helmesjo sf.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib / ...作为断开链接的解决方案:尝试从根目录Luke中搜索。
安德里(Andry)

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_type并且arg1_type 将满足目的。由于ideone似乎不提供C ++ 11模式的增强功能,对不起,我无法发布实际代码。


1
我认为,这是一个好的开始。+1。现在,我们需要处理函数类型以提取所需的信息。(我现在不想使用Boost,因为我想学习这些东西)。
纳瓦兹

6

@KennyTM答案中显示的专业化方法可以扩展到涵盖所有情况,包括可变和可变的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)

演示

请注意,不会针对可变参数调整Arity operator()。相反,也可以考虑is_variadic


1

@KennyTMs提供的答案非常有效,但是,如果lambda没有参数,则无法使用索引arg <0>进行编译。如果还有其他人遇到这个问题,我有一个简单的解决方案(比使用SFINAE相关的解决方案还简单)。

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

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

由于arity不依赖于模板参数的实际数量,因此实际值不会是错误的,并且如果它为0,则至少arg <0>仍然存在,您可以使用它进行处理。如果您已经计划不超过该索引,arg<arity-1>那么它就不会干扰您当前的实现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.