2015-10-05 9 views
21

Ich habe eine Klasse mit typename T Templatet. Es enthält eine Funktion,Warum fördert meine Vorlagenfunktion 'int' nicht zu 'T', wo 'T' = 'doppelt' ist?

template <typename T, size_t a> 
myClass<T,a> operator+(myClass<T,a> lhs, const T& rhs) { 
    return lhs += rhs; 
} 

myClass<T,a> myClass<T,a>::operator+=(const T& rhs) { 
    // Do addition, depends on 'a'. 
    return *this; 
} 

Als ich das nennen mit zum Beispiel

myClass<double, 2> myObj_double_2(constructor args); 
myObj_double_2 = myObj_double_2 + 5.2; 

ich kein Problem haben.

Wenn ich aber

nennen
myObj_double_2 = myObj_double_2 + 5; 

Dann wird der Compiler gibt mir eine Nachricht wie - No match for 'operator+' (operand types are 'myClass<double, 2ul>' and 'int'). Candidates are ... note: deduced conflicting types for parameter 'const T' ('double' and 'int').

Kann ich den Code in irgendeiner Weise schreiben, damit zusätzliche Typen übergeben werden können, die eine Konvertierung in T haben (da beispielsweise double (5) ein gültiger Konstruktoraufruf ist)?

+0

versuchen 'myObj_double_2 = myObj_double_2 + 5.0;' – 101010

+1

Ja - das wird ohne Probleme funktionieren, aber es löst nicht das Problem, das ich fragte (obwohl es die einfache und offensichtliche Lösung ist) – chrisb2244

+1

No int-> double " Promotion "existiert. –

Antwort

32

Wenn Sie Template-Argument Abzug verwenden, alle Abzüge für einen Template-Parameter muss das gleiche Ergebnis.

In Ihrem Fall die beiden Abzüge für T produzieren double und int, die nicht die gleichen sind, und so schlägt der Abzug fehl.

Was Sie tun können, ist ein Funktionsargument für Template-Argument Abzug verwenden, und machen das andere undeduced:

template <typename T, std::size_t A> 
void foo(myClass<T, A> arg1, typename std::common_type<T>::type arg2); 
//       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 

Beachten Sie, dass std::common_type<T>::type im Wesentlichen nur T, sondern weil die Art der arg2 ist jetzt ein abhängiger Typ (sein Name erscheint rechts von ::), es wird nicht abgeleitet. Daher nimmt nur das erste Argument am Abzug teil und erzeugt T = double eindeutig, und dann hat der zweite Funktionsparameter nur den Typ double, und die üblichen Umwandlungen finden statt.

Als Faustregel gilt, dass die Argumentableitung für die Vorlage nicht :: kreuzt.

+0

Das ist perfekt, aber ich denke du meintest typename std :: common_type :: type'. Ihre Lösung erlaubt auch 'const' und' & 'hinzugefügt zu werden, was großartig ist – chrisb2244

+0

@ chrisb2244: Danke, behoben. Ich überlasse Ihnen die Referenzmodifikationen; Sie sind nicht wesentlich für das Problem. –

+1

'common_type' verfällt den Typ; wahrscheinlich nicht relevant hier, aber ist etwas zu beachten. –

17

Der Compiler auf der Überladungsauflösung scheitert einen richtigen Kandidaten für operator+ weil T zu finden bereits double abgezogen werden und wörtliche 5 eine ganze Zahl. Lösung:

template <typename T1, typename T2, size_t a> 
myClass<T1,a> operator+(myClass<T1,a> lhs, const T2& rhs) { 
    return lhs += T1(rhs); 
} 
+0

Wenn "T1" und "T2" gleich sind, führt dies vermutlich zu einer unnötigen Kopie - wenn der Parameter als Wert genommen wird, ist eine Bewegung möglich (und wäre es im Fall von "int" wahrscheinlich schneller) und 'double'?) – chrisb2244

+4

@ chrisb2244 Sie könnten sich teilweise auf den Fall spezialisieren' T1 == T2' – BeyelerStudios

+0

Konstruktor-artige Umwandlungen sind gefährlich, wenn Sie nur implizite Umwandlungen erlauben wollen, dann initialisieren Sie einen lokalen Typ 'T1'. –

8

Sie stoßen auf Probleme mit dem Vorlagentyp Abzug.

Beide Argumente sind „gleichrangig“ gegeben, wenn der Wert von T herzuleiten, und in diesem Fall sind die beiden Argumente nicht einverstanden sind - man sagt Tint sein sollte, der andere sagt Tdouble sein sollte.

Der richtige Weg, dies zu beheben, ist mit Koenig-Operatoren.

Make += und + und dergleichen friend s Ihrer Klasse und Inline implementieren:

template<class T, size_t a> 
class myClass { 
    // etc 
public: 
    friend myClass operator+(myClass lhs, const T& rhs) { 
    lhs += rhs; 
    return std::move(lhs); 
    } 
    friend myClass& operator+=(myClass& lhs, const T& rhs) { 
    // do addition, depends on `a` 
    return *this; 
    } 
}; 

diese Technik etwas Fremdes tut. Basierend auf dem Schablonentyp der Klasse werden Operatoren erstellt, die nicht template sind. Diese werden dann über ADL (Koenig lookup) gefunden, wenn Sie + oder += aufrufen.

Sie erhalten einen dieser Operatoren pro Vorlageninstanziierung, aber sie sind keine Vorlagenoperatoren, daher wird const T& nicht abgeleitet, und die Konvertierung erfolgt wie erwartet.