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

后端 未结 4 1812
[愿得一人]
[愿得一人] 2020-11-22 02:08

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

Basically, I want lambda_traits which can be used in fol

相关标签:
4条回答
  • 2020-11-22 02:17

    Funny, I've just written a function_traits implementation based on Specializing a template on a lambda in C++0x which can give the parameter types. The trick, as described in the answer in that question, is to use the decltype of the lambda's 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;
    }
    

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

    0 讨论(0)
  • 2020-11-22 02:26

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

    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_type and arg1_type will meet the purpose. Since ideone seems not to provide boost in C++11 mode, I couldn't post the actual code, sorry.

    0 讨论(0)
  • 2020-11-22 02:29

    The specialization method shown in @KennyTMs answer can be extended to cover all cases, including variadic and mutable 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)
    

    Demo.

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

    0 讨论(0)
  • 2020-11-22 02:33

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

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

    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.

    0 讨论(0)
提交回复
热议问题