S
DeepCopy schrieb:
Die Deduktion scheint immer angewandt zu werden, egal ob das mit den Funktionssignaturen nachher kompatibel ist oder nicht, d.h. wenn ich ein int übergebe und mein Parameter will T*, wird sich der Compiler nach der Deduktion beschweren.
Er wird sich nur dann beschweren, wenn es sonst keine andere Funktion gibt, die aufgerufen werden kann. Es können auch nicht alle Typen deduziert werden. Beispiel:
template<typename T> struct identity {typedef T type;};
template<typename T> void foo(typename identity<T>::type x);
int main() {
foo(4); // Fehler! Compiler kann T nicht deduzieren, ignoriert
// deswegen das Funktions-Template und findes keine
// andere Funktion, die aufgerufen werden kann.
foo<int>(23); // OK!
}
"identity<T>::type" ist für T kein "deduzierbarer Kontext". Wichtig ist auch, dass wenn der Compiler versucht Template-Parameter zu deduzieren, und ein zu deduzierender Template-Parameter in mehreren "deduzierbaren Kontexten" auftaucht, die Dedukition fehlschlägt, falls er auf unterschiedliche Ergebnisse kommt. Beispiel
template<typename T> void bar(T x, T y);
template<typename T> void baz(T x, typename identity<T>::type y);
int main() {
bar(3,24L); // Fehler! T nicht deduzierbar, weil einmal T=int
// und dann T=long rauskommt
baz(3,24L); // OK, T=int, 24L wird zu einem int implizit konvertiert.
}
DeepCopy schrieb:
So weit, so gut. Aber: was ist wenn als Argument int& und Parameter T* gefordert sind, oder auch anderes herum, wenn int* als Argument und Parameter T& gefordert werden? Hier kann ich nicht "Kürzen" da einmal eine Referenz und einmal ein Zeiger vorhanden ist, was ist das Ergebnis der Deduktion? Bleibt "int*" oder "int&", darf hier die Spezialisierung angewendet werden?
Da gibt es so einen Begriff: SFINAE = substitution failure is not an error. Wenn das Deduzieren nicht gelingt, wird das Template einfach ignoriert, ohne einen Fehler zu generieren. Wenn allerdings keine aufrufbare Funktion bzw Spezialisierung übrig bleibt und das "overload set" leer ist, gibt es natürlich einen Fehler beim Kompilieren.
Top-Level Referenzen werden mehr oder weniger bei der Deduktion ignoriert, bis auf dass array-to-pointer decay bzw function-to-pointer decay nicht sofort angewendet wird:
template<typename T> void dings(T);
template<typename T> void blah (T*);
template<typename T> void bums (T&);
int main()
{
int array[5];
dings(array); // T = int* --> void dings(int*);
blah (array); // T = int --> void blah (int*);
bums (array); // T = int[5] --> void bums (int(&)[5]);
}
"X-to-pointer decays", "implicit cv qualification conversion" und Konvertierung von Derived auf Base sind zulässig bei der Deduktion, alle anderen impliziten Konvertieren nicht.
Edit: Um die Beispiele zu vervollständigen:
template<typename T> struct A {};
struct B : A<int> {};
template<typename T>
void foo(A<T> const& x) {}
int main() {
B b;
foo(b); // T = int
}
"A<T> const&" ist für T ein deduzierbarer Kontext und die Deduktion gelingt auch dann, wenn foo mit einem abgeleiteten Objekt aufgerufen wird. Hier findet sowohl eine Derived-to-Base Konvertierung als auch eine "cv qualification conversion" statt.
Wer sich für all die Details interessiert, sollte sich C++ Templates | ISBN: 9780201734843 mal anschauen. Ich kann das Buch nur empfehlen. Mir sind so einige Lichter beim Lesen aufgegangen.
Gruß,
SP