
Nawaz
Ist es bei einem Lambda möglich, dessen Parametertyp und Rückgabetyp herauszufinden? Wenn ja, wie?
Grundsätzlich möchte ich lambda_traits
die auf folgende Weise verwendet werden können:
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
Die Motivation dahinter ist, dass ich sie nutzen möchte lambda_traits
in einer Funktionsvorlage, die ein Lambda als Argument akzeptiert, und ich muss den Parametertyp und den Rückgabetyp innerhalb der Funktion kennen:
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!
//...
}
Vorerst können wir davon ausgehen, dass das Lambda genau ein Argument akzeptiert.
Anfangs habe ich versucht, mit zu arbeiten std::function
als:
template<typename T>
A<T> f(std::function<bool(T)> fun)
{
return A<T>(fun);
}
f([](int){return true;}); //error
Aber es würde offensichtlich Fehler geben. Also habe ich es geändert TLambda
Version der Funktionsvorlage und wollen die konstruieren std::function
Objekt innerhalb der Funktion (wie oben gezeigt).

kennytm
Komisch, ich habe gerade einen geschrieben function_traits
Implementierung beyogen auf Spezialisieren eines Templates auf ein Lambda in C++0x die die Parametertypen geben können. Der Trick, wie in der Antwort auf diese Frage beschrieben, besteht darin, die zu verwenden decltype
der Lambdas 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;
}
Beachten Sie, dass diese Lösung nicht Arbeit für generisches Lambda wie [](auto x) {}
.
Obwohl ich nicht sicher bin, ob dies streng standardkonform ist,
Idee folgenden Code kompiliert:
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, "" );
}
Diese liefert jedoch nur den Funktionstyp, sodass Ergebnis- und Parametertypen daraus extrahiert werden müssen. Wenn Sie verwenden können boost::function_traits
, result_type
und arg1_type
wird den Zweck erfüllen. Da ideone im C++11-Modus anscheinend keinen Boost bietet, konnte ich den eigentlichen Code leider nicht posten.

Columbo
Die in @KennyTMs Antwort gezeigte Spezialisierungsmethode kann erweitert werden, um alle Fälle abzudecken, einschließlich variadischer und veränderlicher 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.
Beachten Sie, dass die Arität nicht für Variadic angepasst wird operator()
S. Stattdessen kann man auch überlegen is_variadic
.
Die Antwort von @KennyTMs funktioniert hervorragend, aber wenn ein Lambda keine Parameter hat, wird die Verwendung des Index arg<0> nicht kompiliert. Wenn jemand anderes dieses Problem hatte, habe ich eine einfache Lösung (einfacher als die Verwendung von SFINAE-bezogenen Lösungen).
Fügen Sie einfach void am Ende des Tupels in der arg-Struktur nach den variadischen Argumenttypen hinzu. dh
template <size_t i>
struct arg
{
typedef typename std::tuple_element<i, std::tuple<Args...,void>>::type type;
};
Da die arität nicht von der tatsächlichen Anzahl der Vorlagenparameter abhängt, ist die tatsächliche nicht falsch, und wenn sie 0 ist, wird zumindest arg<0> noch vorhanden sein, und Sie können damit machen, was Sie wollen. Wenn Sie bereits planen, den Index nicht zu überschreiten arg<arity-1>
dann sollte es Ihre aktuelle Implementierung nicht stören.
9974300cookie-checkIst es möglich, den Parametertyp und den Rückgabetyp eines Lambda herauszufinden?yes
Wenn Sie den Parametertyp kennen, kann dies verwendet werden, um den Rückgabetyp herauszufinden. Ich weiß jedoch nicht, wie ich den Parametertyp herausfinden soll.
– Mankarse
30. Oktober 2011 um 5:57 Uhr
Wird davon ausgegangen, dass die Funktion ein einzelnes Argument benötigt?
– iammilind
30. Oktober 2011 um 6:01 Uhr
“Parametertyp” Aber eine beliebige Lambda-Funktion hat keinen Parametertyp. Es könnte eine beliebige Anzahl von Parametern annehmen. Daher müsste jede Merkmalsklasse so konzipiert sein, dass sie Parameter nach Positionsindizes abfragt.
– Nicol Bolas
30. Oktober 2011 um 6:01 Uhr
@iammilind: Ja. davon können wir vorerst ausgehen.
– Nawaz
30. Oktober 2011 um 6:03 Uhr
@NicolBolas: Derzeit können wir davon ausgehen, dass das Lambda genau ein Argument akzeptiert.
– Nawaz
30. Oktober 2011 um 6:03 Uhr