2013-03-15 13 views
24

Już kilka razy użyłem idiomu SFINAE i przyzwyczaiłem się do umieszczenia mojego std::enable_if<> w parametrach szablonu, a nie w typach zwracanych. Jednak natrafiłem na jakiś trywialny przypadek, w którym to nie zadziałało, i nie jestem pewien dlaczego. Przede wszystkim, tutaj jest mój główny:SFINAE działa w typie zwrotnym, ale nie jako parametr szablonu

int main() 
{ 
    foo(5); 
    foo(3.4); 
} 

Oto implementacja foo że wyzwala błąd:

template<typename T, 
     typename = typename std::enable_if<std::is_integral<T>::value>::type> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm an integer!\n"; 
} 

template<typename T, 
     typename = typename std::enable_if<std::is_floating_point<T>::value>::type> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 

I tu jest podobno odpowiednik kawałek kodu, który działa dobrze:

template<typename T> 
auto foo(T) 
    -> typename std::enable_if<std::is_integral<T>::value>::type 
{ 
    std::cout << "I'm an integrer!\n"; 
} 

template<typename T> 
auto foo(T) 
    -> typename std::enable_if<std::is_floating_point<T>::value>::type 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 

Moje pytanie brzmi: dlaczego pierwsza implementacja foo wyzwala ten błąd, podczas gdy druga nie uruchamia go?

main.cpp:14:6: error: redefinition of 'template<class T, class> void foo(T)' 
auto foo(T) 
    ^
main.cpp:6:6: note: 'template<class T, class> void foo(T)' previously declared here 
auto foo(T) 
    ^
main.cpp: In function 'int main()': 
main.cpp:23:12: error: no matching function for call to 'foo(double)' 
    foo(3.4); 
      ^
main.cpp:6:6: note: candidate: template<class T, class> void foo(T) 
auto foo(T) 
    ^
main.cpp:6:6: note: template argument deduction/substitution failed: 
main.cpp:5:10: error: no type named 'type' in 'struct std::enable_if<false, void>' 
      typename = typename std::enable_if<std::is_integral<T>::value>::type> 
     ^

EDIT:

Working code i faulty code.

+1

Ok. Rzeczywista demonstracja: [1. nie udało się skompilować części] (http://ideone.com/mJ8Zp6) i [druga pomyślnie skompilowana część] (http://ideone.com/G0jBft). –

+0

Dodatkowe informacje: to samo z VS 2012 CTP w listopadzie. –

+3

[This] (http://flamingdangerzone.com/cxx11/2012/06/01/almost-static-if.html) powinien być dla Ciebie idealny. – Xeo

Odpowiedz

26

Powinieneś spojrzeć na 14.5.6.1 Function template overloading (standard C++ 11), w którym zdefiniowano równoważność szablonów funkcji. W skrócie, domyślne argumenty szablonów nie są brane pod uwagę, więc w pierwszym przypadku masz ten sam szablon funkcji zdefiniowany dwa razy. W drugim przypadku masz wyrażenia odnoszące się do parametrów szablonu używanych w typie zwrotnym (ponownie patrz 14.5.6.1/4). Ponieważ to wyrażenie jest częścią podpisu, otrzymujesz dwie różne deklaracje szablonu funkcji, a zatem SFINAE ma szansę pracować.

+0

Dziękuję bardzo. To wyjaśnienie jest co najmniej proste i jasne. Nie miałem pojęcia o tej zasadzie :) – Morwenn

7

Szablon = ... po prostu podaje domyślny parametr. To nie jest częścią rzeczywistego podpisu, który wygląda

template<typename T, typename> 
auto foo(T a); 

dla zarówno funkcje.

W zależności od potrzeb, najbardziej ogólnym rozwiązaniem tego problemu jest używanie wysyłania tagów.

struct integral_tag { typedef integral_tag category; }; 
struct floating_tag { typedef floating_tag category; }; 

template <typename T> struct foo_tag 
: std::conditional<std::is_integral<T>::value, integral_tag, 
        typename std::conditional<std::is_floating_point<T>::value, floating_tag, 
               std::false_type>::type>::type {}; 

template<typename T> 
T foo_impl(T a, integral_tag) { return a; } 

template<typename T> 
T foo_impl(T a, floating_tag) { return a; } 

template <typename T> 
T foo(T a) 
{ 
    static_assert(!std::is_base_of<std::false_type, foo_tag<T> >::value, 
       "T must be either floating point or integral"); 
    return foo_impl(a, typename foo_tag<T>::category{}); 
} 

struct bigint {}; 
template<> struct foo_tag<bigint> : integral_tag {}; 

int main() 
{ 
    //foo("x"); // produces a nice error message 
    foo(1); 
    foo(1.5); 
    foo(bigint{}); 
} 
+2

To nie jest bardzo ogólne - jest ściśle związane z tym przypadkiem - integer vs floating. – einpoklum

2

Wartości w szablonach pracy:

template<typename T, 
     typename std::enable_if<std::is_integral<T>::value, int>::type = 0> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm an integer!\n"; 
} 

template<typename T, 
     typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> 
auto foo(T) 
    -> void 
{ 
    std::cout << "I'm a floating point number!\n"; 
} 
Powiązane problemy