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

问题描述 投票:0回答:6

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

c++ lambda metaprogramming c++11 traits
6个回答
181
投票

有趣,我刚刚编写了一个

function_traits
实现,基于 在 C++0x 中专门针对 lambda 的模板,它可以给出参数类型。正如该问题的答案中所述,技巧是使用 lambda 的
decltype
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;
}

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

等通用 lambda。


12
投票
虽然我不确定这是否严格符合标准,

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_traits

result_type
arg1_type
就会达到目的。
由于 ideone 似乎不提供 C++11 模式下的提升,我无法发布
实际代码,抱歉。


9
投票
@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)

演示

请注意,可变参数

operator()

 的数量不会调整。相反,也可以考虑
is_variadic


2
投票
@KennyTMs 提供的答案效果很好,但是如果 lambda 没有参数,则使用索引 arg

无法编译。如果其他人遇到这个问题,我有一个简单的解决方案(比使用 SFINAE 相关解决方案更简单)。 <0>

只需将 void 添加到 arg 结构体中元组的末尾,位于可变参数类型之后。即

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>

,那么它不应该干扰您当前的实施。


0
投票
如果您正在寻找可调用的 C++ 中所有类型的完整解决方案,其中很多答案都有效,但错过了一些极端情况,例如

    对 lambda 的引用
  • 函数和函数指针
这是我所知的完整解决方案(通用 lambda 除外) - 如果有任何遗漏,请在评论中告诉我:

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

的灵感来自于
此代码


0
投票
这是我的函数特征的玩具版本。我利用了

std::function

 的类型推断,但没有实际构建任何 
std::function
 对象。

这允许使用常规函数,而不仅仅是函子。

template <typename Return, typename... Args> struct FunctionTraits_ { FunctionTraits_(std::function<Return(Args...)> f) {} using return_type = Return; using arg_types = std::tuple<Args...>; enum { arity = sizeof...(Args) }; template <size_t i> struct arg_type { using type = typename std::tuple_element<i, arg_types>::type; }; }; template <typename Function> struct FunctionTraits : public decltype(FunctionTraits_( std::function{std::declval<Function>()})) {}; // Testing std::size_t func(int, const std::string &s) { return s.size(); } auto lambda = [](int, const std::string &s) { return s.size(); }; int main() { using traits = FunctionTraits<decltype(func)>; // using traits = FunctionTraits<decltype(lambda)>; static_assert(traits::arity == 2); static_assert(std::is_same_v<traits::return_type, std::size_t>); static_assert(std::is_same_v<traits::arg_type<0>::type, int>); static_assert(std::is_same_v<traits::arg_type<1>::type, const std::string &>); static_assert(std::is_same_v<traits::arg_types, std::tuple<int, const std::string &>>); return 0; }
谢谢,

Cœur,给我灵感。

© www.soinside.com 2019 - 2024. All rights reserved.