Можно ли определить тип параметра и возвращаемый тип лямбды?
учитывая лямбда, можно ли выяснить, что это тип параметра и тип возврата? Если да, то как?
в принципе, я хочу, чтобы 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_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!
//...
}
На данный момент, мы можем предположить, что лямбда принимает ровно аргумент.
сначала я пытался работать с std::function
как:
template<typename T>
A<T> f(std::function<bool(T)> fun)
{
return A<T>(fun);
}
f([](int){return true;}); //error
но это, очевидно, даст ошибку (ideone). Поэтому я изменил его на TLambda
версия шаблона функции и хотите построить std::function
объект внутри функции (как показано выше).
4 ответа:
смешно, я только что написал
function_traits
реализация на основе специализируясь шаблон на лямбда в C++0x который может дать типы параметров. Трюк, как описано в ответе на этот вопрос, заключается в использовании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) {}
.
хотя я не уверен, что это строго соответствует стандарту, 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, я не мог опубликовать фактический код, извините.
метод специализации, показанный в ответе @KennyTMs, может быть расширен для охвата всех случаев, включая вариативные и изменяемые лямбды:
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)
демо.
обратите внимание, что arity не настраивается для variadic
operator()
s. Вместо этого можно также рассмотретьis_variadic
.
ответ, предоставленный @KennyTMs, отлично работает, однако если лямбда не имеет параметров, использование индекса arg не компилируется. Если у кого-то еще была эта проблема, у меня есть простое решение (проще, чем использовать решения, связанные с SFINAE).
просто добавить пустоты в конце кортежа арг структуры после с переменным числом аргументов типы аргументов. то есть
template <size_t i> struct arg { typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type; };
поскольку arity не зависит от фактического количества параметров шаблона, фактический не будет неверно, и если это 0, то по крайней мере arg все еще будет существовать, и вы можете делать с ним все, что захотите. Если вы уже планируете не превышать индекс
arg<arity-1>
тогда это не должно мешать вашей текущей реализации.