2016-11-03 22 views
8

Potrzebuję metody określania typów argumentów funkcji, dlatego napisałem klasę closure_traits, podaną poniżej, zainspirowaną przez Is it possible to figure out the parameter type and return type of a lambda?.C++ Lambda nie ma operatora()

Jednak gdy próbuję zastosować to do prostej wartości lambda, pojawia się błąd, że "operator()" nie jest członkiem "(typ lambda)". Jednak, zgodnie z cppreference, lambda ma operatora(). Próbowałem także używać funkcji std :: i otrzymałem błąd równoważny. Chyba nie jestem pewien, co się dzieje, a każda pomoc będzie bardzo ceniona.

#include<type_traits> 
#include<tuple>                   
#include<utility> 
#include<iostream>                          

/* For generic types use the type signature of their operator() */              
template <typename T>                      
struct closure_traits : public 
         closure_traits<decltype(&T::operator())> {};           

/* Otherwise, we do a template match on a function type. */ 
template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args)>             
{ 
    using arity = std::integral_constant<std::size_t, 
             sizeof...(ArgTypes)>;            
    using Ret = ReturnType;         

    /* The argument types will be the same as the types of the 
    * elements of a tuple composed of them. 
    */                        
    template <std::size_t I>  
    struct Args {   
     using type = typename std::tuple_element<I, 
             std::tuple<ArgTypes...>>::type;                              
    };                                                           

};                                                             

int main() {                              
    auto thing = [=] (int x) {return x;}; 

    std::cerr << "The number of arguments is " 
       << closure_traits<decltype(thing)>::arity << std::endl;                              

    return 0;                              
}  

Komunikaty o błędach kompilatora, które dostaję, znajdują się poniżej. Moja komenda kompilacji to po prostu g ++ -std = C++ 14 main.cpp.

main.cpp: In instantiation of ‘struct closure_traits<int (main()::<lambda(int)>::*)(int) const>’: 
main.cpp:9:8: required from ‘struct closure_traits<main()::<lambda(int)> >’ 
main.cpp:34:82: required from here 
main.cpp:9:56: error: ‘operator()’ is not a member of ‘int (main()::<lambda(int)>::*)(int) const’ 
struct closure_traits : public closure_traits<decltype(&T::operator())> {};           
                ^
main.cpp: In function ‘int main()’: 
main.cpp:34:51: error: ‘arity’ is not a member of ‘closure_traits<main()::<lambda(int)> >’ 
std::cerr << "The number of arguments is " << closure_traits<decltype(thing)>::arity << std::endl; 
+0

Wiecie, dlaczego jest to zły pomysł w C++ 14, ze względu na argumenty "auto" na szablonach, prawda? Pytanie "ile argumentów tego typu" jest często lepszym pomysłem. – Yakk

+0

Nie jestem pewien, czy rozumiem twoje pytanie. Próbuję wywnioskować liczbę argumentów funkcji i ich typów. Dlaczego miałoby to wpływać na auto? – sangrey

+1

Twój projekt zasadniczo nie może obsłużyć '[] (auto x) {std :: cout << x <<" \ n ";}'. Callables w C++ nie mają ustalonej liczby argumentów, a ich argumenty nie mają stałych typów. To jest jak próba określenia podstawy 'int' w C++: koncepcja tak naprawdę nie ma zastosowania, ponieważ' int' nie * ma * podstawową.W twoim przypadku za * niektóre * kalki możesz mieć ustalone typy arii i argumentów, co dezorientuje problem. – Yakk

Odpowiedz

8

Twoja specjalizacja nie jest zgodna z argumentem decltype(&T::operator()).

Z tego powodu, zamiast wybierać specjalizację (zgodnie z jej potrzebami), kompilator jest zmuszony do rekurencyjnego wyboru tego samego głównego szablonu. Co sprawia, że ​​ponownie stosuje ono wyrażenie &T::operator(), po tym, jak zostało już raz zastosowane. Np. Pierwsza próba wykonania &T::operator() faktycznie się powiedzie, ale wtedy kompilator ponownie spróbuje zastosować &T::operator(), gdy T jest już int (main()::<lambda(int)>::*)(int) const. Ten drugi, oczywiście, nie ma operator(), dlatego otrzymujesz ten komunikat o błędzie.

Przyczyna, dla której nie można wybrać specjalizacji, jest const w deklaracji parametru szablonu. Model lambda operator() jest faktycznie członkiem klasy lambda const. Dodaj const do zgłoszenia swojej specjalizacji

template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const> 
... 

kompilator będzie podążać ścieżką specjalizacji zamierzony go śledzić.

I oczywiście trzeba wydrukować closure_traits<decltype(thing)>::arity::value, a nie tylko closure_traits<decltype(thing)>::arity.

+0

Chyba masz na myśli "specjalizacja nie * nie * mecz" – harmic

+0

Dzięki! To się udało. – sangrey

4

Kompilator poprawnie pobiera operator() typu lambda, ale wskaźnik do tej funkcji składowej nie pasuje do twojej specjalizacji z powodu kwalifikatora const.

Należy dodać drugą specjalizację

template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>             
{ 
    // ... 
}; 

(Tak, pisanie szablonów do podjęcia typu funkcja jest bolesne.)

+1

Pisanie szablonów, które pełnią funkcję, nie jest bolesne: wystarczy użyć np. typename Callable i nie martw się o całą resztę. To samo z nazwą typu Container. – rubenvb

1

W przypadku gdy ktoś ma kłopoty z tym prawidłowy kod jest następujący:

#include<type_traits> 
#include<tuple>                   
#include<utility> 
#include<iostream>                          

/* For generic types use the type signature of their operator() */              
template <typename T>                      
struct closure_traits : public 
         closure_traits<decltype(&T::operator())> {};           

/* Otherwise, we do a template match on a function type. */ 
template <typename ClassType, typename ReturnType, 
      typename... ArgTypes>             
struct closure_traits<ReturnType (ClassType::*) (ArgTypes... args) const>             
{ 
    using arity = std::integral_constant<std::size_t, 
             sizeof...(ArgTypes)>;            
    using Ret = ReturnType;         

    /* The argument types will be the same as the types of the 
    * elements of a tuple composed of them. 
    */                        
    template <std::size_t I>  
    struct Args {   
     using type = typename std::tuple_element<I, 
             std::tuple<ArgTypes...>>::type;                              
    };                                                           

};                                                             

int main() {                              
    auto thing = [=] (int x) {return x;}; 

    std::cerr << "The number of arguments is " 
       << closure_traits<decltype(thing)>::arity::value << std::endl;                              

    return 0;                              
}