给定一个 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
对象(如上所示)。
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。
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 模式下的提升,我无法发布 实际代码,抱歉。
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
。
无法编译。如果其他人遇到这个问题,我有一个简单的解决方案(比使用 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>
,那么它不应该干扰您当前的实施。
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
的灵感来自于此代码。
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,给我灵感。