2012-01-03 22 views
12

Chciałbym w jakiś sposób uzyskać pierwszy typ parametru funkcji lambda, czy jest to możliwe?Uzyskaj typ parametru lambda:

np.

zamiast:

template<typename T> 
struct base 
{ 
    virtual bool operator()(T) = 0; 
} 

template<typename F, typename T> 
struct filter : public base<T> 
{ 
    virtual bool operator()(T) override {return /*...*/ } 
}; 

template<typename T, typename F> 
filter<T> make_filter(F func) 
{ 
     return filter<F, T>(std::move(func)); 
} 

auto f = make_filter<int>([](int n){return n % 2 == 0;}); 

Chciałbym:

template<typename F> 
struct filter : public base<typename param1<F>::type> 
{ 
    bool operator()(typename param1<F>::type){return /*...*/ } 
}; 

template<typename F> 
filter<F> make_filter(F func) 
{ 
     return filter<F>(std::move(func)); 
} 

auto f = make_filter([](int n){return n % 2 == 0;}); 

podstawie odpowiedzi Xeo to jest to co mam pracę w VS2010:

template<typename FPtr> 
struct arg1_traits_impl; 

template<typename R, typename C, typename A1> 
struct arg1_traits_impl<R (C::*)(A1)>{typedef A1 arg1_type;}; 

template<typename R, typename C, typename A1> 
struct arg1_traits_impl<R (C::*)(A1) const>{typedef A1 arg1_type;}; 

template<typename T> 
typename arg1_traits_impl<T>::arg1_type arg1_type_helper(T); 

template<typename F> 
struct filter : public base<typename std::decay<decltype(detail::arg1_type_helper(&F::operator()))>::type> 
{ 
    bool operator()(typename std::decay<decltype(detail::arg1_type_helper(&F::operator()))>::type){return /*...*/ } 
}; 

template<typename T, typename F> 
filter<F> make_filter(F func) 
{ 
     return filter<F>(std::move(func)); 
} 

próbowałem uproszczenie kod, ale każda próba wydaje się go łamać.

+0

Szukałam w jakimś triku z 'std :: fuction :: first_argument_type' jednak VS2010 nie wydaje się implemenet' first_argument_type'. – ronag

+0

Skomplikowane. * Bardzo * skomplikowany, zwłaszcza bez szablonów variadic i VS2010. Będziesz potrzebował pewnego rodzaju funkcji-cech, które rozdzielają rzeczywisty typ wskaźnika funkcji na jego składniki i używają 'function_traits :: param1_type' lub tym podobne. VS2010 ma jednak problemy z tym kodem, niech zobaczę, czy mogę znaleźć moje pytanie na ten temat ... – Xeo

+0

Nie możesz użyć 'std :: function ' do tego, ponieważ 'F' jest typem lambda, a nie podpisem jak 'bool (int)'. – Xeo

Odpowiedz

8

Opcja najprostszy byłoby po prostu zrobić operator() szablon sama:

template<typename F> 
struct filter 
{ 
    template<class Arg> 
    void operator(Arg&& arg){ 
     // use std::forward<Arg>(arg) to call the stored function 
    } 
}; 

template<typename F> 
filter<F> make_filter(F func) 
{ 
     return filter<F>(std::move(func)); 
} 

auto f = make_filter([](int n){return n % 2 == 0;}); 

Teraz teoretycznie poniższy kod powinien prostu pracować. Jednak nie robi z MSVC10 Dzięki bug:

#include <iostream> 
#include <typeinfo> 

template<class FPtr> 
struct function_traits; 

template<class T, class C> 
struct function_traits<T (C::*)> 
{ 
    typedef T type; 
}; 

template<class F> 
void bar(F f){ 
    typedef typename function_traits< 
     decltype(&F::operator())>::type signature; 
    std::cout << typeid(signature).name(); 
} 

int main(){ 
    bar([](int n){ return n % 2 == 0; }); 
} 

Here jest przykładem tego, jak to będzie wyglądać z GCC. Jednak MSVC10 po prostu nie kompiluje kodu. Aby uzyskać więcej informacji, patrz this question of mine. Zasadniczo MSVC10 nie traktuje decltype(&F::operator()) jako typu zależnego. Oto obejście, który został opracowany w chat discussion:

#include <iostream> 
#include <typeinfo> 
#include <type_traits> 

template<class FPtr> 
struct function_traits; 

template<class R, class C, class A1> 
struct function_traits<R (C::*)(A1)> 
{ // non-const specialization 
    typedef A1 arg_type; 
    typedef R result_type; 
    typedef R type(A1); 
}; 

template<class R, class C, class A1> 
struct function_traits<R (C::*)(A1) const> 
{ // const specialization 
    typedef A1 arg_type; 
    typedef R result_type; 
    typedef R type(A1); 
}; 

template<class T> 
typename function_traits<T>::type* bar_helper(T); 

template<class F> 
void bar(F f){ 
    typedef decltype(bar_helper(&F::operator())) fptr; 
    typedef typename std::remove_pointer<fptr>::type signature; 
    std::cout << typeid(signature).name(); 
} 

int main(){ 
    bar([](int n){ return n % 2 == 0; }); 
} 
+0

Świetnie! Co jednak z tym, że funkcja filter :: operator() jest wirtualna? – ronag

+0

@ronag: Wtedy masz problem. : | Dlaczego musiałby on być "wirtualny", gdyby sama klasa była szablonowana na typie funkcji/funktora? – Xeo

+0

Ponieważ jest to uproszczony przykład, w moim rzeczywistym kodzie filtr ma klasę podstawową. – ronag