是否可以确定 lambda 的参数类型和返回类型?
Posted
技术标签:
【中文标题】是否可以确定 lambda 的参数类型和返回类型?【英文标题】:Is it possible to figure out the parameter type and return type of a lambda? 【发布时间】:2011-12-18 02:46:25 【问题描述】:给定一个 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
对象(如上图)。
【问题讨论】:
如果你知道参数类型,那么this可以用来确定返回类型。我不知道如何弄清楚参数类型。 是否假定函数采用单个参数? "参数类型" 但是任意 lambda 函数没有参数类型。它可以采用任意数量的参数。因此,任何特征类都必须设计为按位置索引查询参数。 @iammilind:是的。目前,我们可以假设。 @NicolBolas:暂时,我们可以假设 lambda 只接受一个参数。 【参考方案1】:有趣的是,我刚刚基于 Specializing a template on a lambda in C++0x 写了一个function_traits
implementation,它可以给出参数类型。如该问题的答案所述,诀窍是使用 lambda 的 operator()
的 decltype
。
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。
【讨论】:
嘿,我只是在写这个。没想到tuple_element
,谢谢。
@GMan:如果您的方法与此不完全相同,请发布。我要测试这个解决方案。
一个完整的 trait 也将使用非 const
的特化,对于那些声明为 mutable
([]() mutable -> T ...
) 的 lambda。
@Andry 对于具有(可能)多个operator()
重载的函数对象来说,这是一个根本问题,而不是这个实现。 auto
不是类型,所以它永远不可能是 traits::template arg<0>::type
的答案
@helmesjo sf.net/p/tacklelib/tacklelib/HEAD/tree/trunk/include/tacklelib/… 作为断开链接的解决方案:尝试从根目录搜索,Luke。【参考方案2】:
虽然我不确定这是否严格符合标准, 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 模式下提供提升,所以我无法发布
实际代码,抱歉。
【讨论】:
我认为,这是一个好的开始。为此+1。现在我们需要处理函数类型以提取所需的信息。 (我现在不想使用 Boost,因为我想学习这些东西)。【参考方案3】:@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()
s 调整元数。相反,也可以考虑is_variadic
。
【讨论】:
【参考方案4】:@KennyTMs 提供的答案效果很好,但是如果 lambda 没有参数,则使用索引 arg 不会编译。如果其他人遇到这个问题,我有一个简单的解决方案(比使用 SFINAE 相关的解决方案更简单,也就是说)。
只需在可变参数类型之后的 arg 结构中的元组末尾添加 void。即
template <size_t i>
struct arg
typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type;
;
由于 arity 不依赖于模板参数的实际数量,实际不会不正确,如果它为 0,那么至少 arg 仍然存在,您可以随心所欲地使用它。如果您已经计划不超过索引arg<arity-1>
,那么它不应该干扰您当前的实现。
【讨论】:
以上是关于是否可以确定 lambda 的参数类型和返回类型?的主要内容,如果未能解决你的问题,请参考以下文章