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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7943525/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 17:45:29  来源:igfitidea点击:

Is it possible to figure out the parameter type and return type of a lambda?

c++lambdametaprogrammingc++11traits

提问by Nawaz

Given a lambda, is it possible to figure out it's parameter type and return type? If yes, how?

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

Basically, I want lambda_traitswhich can be used in following ways:

基本上,我希望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

The motivation behind is that I want to use lambda_traitsin a function template which accepts a lambda as argument, and I need to know it's parameter type and return type inside the function:

背后的动机是我想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!
   //...
}

For the time being, we can assume that the lambda takes exactly one argument.

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

Initially, I tried to work with std::functionas:

最初,我尝试使用以下方式工作std::function

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

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

But it obviously would give error. So I changed it to TLambdaversion of the function template and want to construct the std::functionobject inside the function (as shown above).

但它显然会出错。所以我把它改成TLambda了函数模板的版本,想在函数std::function内部构造对象(如上图)。

回答by kennytm

Funny, I've just written a function_traitsimplementationbased on Specializing a template on a lambda in C++0xwhich can give the parameter types. The trick, as described in the answer in that question, is to use the decltypeof the lambda's operator().

有趣的是,我刚刚编写了一个基于在 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;
}

Note that this solution does notwork for generic lambda like [](auto x) {}.

请注意,此解决方案不适用于像[](auto x) {}.

回答by Ise Wisteria

Though I'm not sure this is strictly standard conforming, ideonecompiled the following code:

虽然我不确定这是否严格符合标准,但 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, "" );
}

However, this provides only the function type, so the result and parameter types have to be extracted from it. If you can use boost::function_traits, result_typeand arg1_typewill meet the purpose. Since ideone seems not to provide boost in C++11 mode, I couldn't post the actual code, sorry.

但是,这仅提供了函数类型,因此必须从中提取结果和参数类型。如果可以使用boost::function_traitsresult_type并且arg1_type将满足目的。由于 ideone 似乎没有在 C++11 模式下提供提升,我无法发布实际代码,抱歉。

回答by Columbo

The specialization method shown in @KennyTMs answer can be extended to cover all cases, including variadic and mutable lambdas:

@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.

演示

Note that the arity is not adjusted for variadic operator()s. Instead one can also consider is_variadic.

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

回答by Jon Koelzer

The answer provided by @KennyTMs works great, however if a lambda has no parameters, using the index arg<0> does not compile. If anyone else was having this problem, I have a simple solution (simpler than using SFINAE related solutions, that is).

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

Just add void to the end of the tuple in the arg struct after the variadic argument types. i.e.

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

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

since the arity isn't dependent on the actual number of template parameters, the actual won't be incorrect, and if it's 0 then at least arg<0> will still exist and you can do with it what you will. If you already plan to not exceed the index arg<arity-1>then it shouldn't interfere with your current implementation.

由于 arity 不依赖于模板参数的实际数量,因此实际值不会不正确,如果它是 0,那么至少 arg<0> 仍然存在,您可以随心所欲地使用它。如果您已经计划不超过索引,arg<arity-1>那么它不应该干扰您当前的实施。