2016-09-20 2 views
1

Warum Compiler T mit diesem Code ableiten kann:C++ Template-Parameter Abzug nicht

#include <vector> 

template<typename T> 
void foo(T& t) {} 

int main(void) { 
    std::vector<uint8_t> vec = { 1,2,3 }; 
    foo(vec); 
    return 0; 
} 

Aber nicht mit diesem Code:

#include <vector> 
#include <type_traits> 

template<typename T> 
void foo(typename std::enable_if<true, T>::type& t) {} 

int main(void) { 
    std::vector<uint8_t> vec = { 1,2,3 }; 
    foo(vec); 
    return 0; 
} 

I zweites Konstrukt verwendet werden soll, zwischen zwei Template-Funktionen wählen basierend auf der Existenz der bestandenen Klassenmethode.

+2

Es könnte eine Spezialisierung 'std :: enable_if ' sein, die einen 'mit type = std :: vector enthält '. Wie kann der Compiler wissen, wann er nur den Parametertyp hat? –

Antwort

4

Wie vsoftco erklärt, Sie haben einen nicht abgeleitet Kontext.

Für SFINAE Sie eine der folgenden Optionen verwenden:

template<typename T> 
std::enable_if_t<condition_dependent_of_T, ReturnType> 
foo(T& t) {} 

oder

template<typename T, std::enable_if_t<condition_dependent_of_T>* = nullptr> 
ReturnType foo(T& t) {} 
+0

Es funktioniert super. Aber ich kann nicht verstehen, warum condition_dependent_of_T wirklich von T abhängen muss. Wenn ich dort ausgewertete Konstanten einstelle, kann der Code nicht kompiliert werden, warum ist das so? – omicronns

+2

@omicronns 'T' wird aus dem Argument' T & t' abgeleitet, nicht aus 'enable_if'. Die nicht-abgeleiteten Kontexte sind nicht ableitbar, da es für einen Compiler extrem schwierig (und manchmal unmöglich) ist, den Typ von ihnen abzuleiten. Die 'enable_if' ** ** sollte, hängt von' T' als 'T' wird abgeleitet werden, dann wird SFINAE in kicken. – vsoftco

+0

@ Jarod42 ich einen Wunsch Antworten fusionieren kann auf SO :) – vsoftco

6

Im zweiten Fall, dass Sie eine non-deduced context haben, mit anderen Worten, kann der Compiler den Typ nicht ableiten.

Das einfachste Beispiel eines nicht abgeleitet Kontext ist

template<typename T> 
struct Id 
{ 
    using type = T; 
}; 

template<typename T> 
void foo(typename Id<T>::type arg); // T cannot be deduced 
+2

Um das Problem zu lösen, muss der Kontext für den Abzug und enable_if getrennt werden. Gängige Praxis ist in der Funktion Rückgabewert zu verwenden enable_if, wie folgt aus: 'template Typname std :: enable_if > :: Wert, leer> :: Typ foo (T & t) {} ' –

2

Zur Visualisierung des Problems lassen Sie uns ein Beispiel analysieren:

template <class> 
struct foo { 
    using type = float; 
}; 

template <> 
struct foo<bool> { 
    using type = int; 
}; 

template <> 
struct foo<int> { 
    using type = int; 
}; 

template <class T> 
void bar(foo<T>::type t) { } 

int main() { 
    bar(int{}); 
} 

Jetzt in der Leitung bar(int{}); beide Typen bool sowie int entspricht einer Temp späte Parameter T. Welcher Wert sollte dann abgeleitet werden? Dies ist nur das eine Beispiel, warum ein nicht-abgeleiteter Kontext unbedingt notwendig ist!

+1

upvoted für das sehr schöne Beispiel! – vsoftco

+0

Ok. Aber die Compiler kennen diese mehrdeutigen Spezialisierungen per Bar-Call. Wenn foo nicht vorhanden ist (oder foo ), dann gibt es keine Mehrdeutigkeit. – omicronns

+1

@omicronns Ja, aber stell dir vor, wie viele Regeln und Ausnahmen zu einem bereits sehr komplizierten Regelwerk hinzugefügt werden müssen. Und manchmal weiß der Compiler nicht: Was ist, wenn Ihre Spezialisierungen in verschiedenen Kompilierungseinheiten definiert sind? Der Compiler macht keine Annahmen über andere Einheiten. – vsoftco