Partial ordering of function templates and default arguments
-
Ist das zweite Funktionstemplate unten "more specialized" (C++ Standard, 14.5.5.2 Partial ordering of function templates) als das erste?:
template <typename T> void f(T const&, T* = 0) {} template <typename T> void f(T const&, T** = 0) {}Wenn ja, warum wird folgendes Programm nicht kompiliert?:
template <typename T> void f(T const&, T* = 0) {} template <typename T> void f(T const&, T** = 0) {} int main() { int i; f(i); // VC++ 8.0 - 'f' : ambiguous call to overloaded function // Comeau C/C++ 4.3.3 - more than one instance of overloaded function "f" }Wenn nein, warum wird folgendes Programm kompiliert?:
template <typename T> void f(T const&, T* = 0) {} template <typename T> void f(T const&, T** = 0) {} int main() { int i; f(i, (int**) 0); }Oder hängt es auch vom Aufruf ab, ob ein Funktionstemplate "more specialized" als ein anderes ist? Aber im C++-Standard steht "A template is more specialized than another ..." und nicht etwa "A template using is more specialized ...", und das einzige, was indirekt sagt, dass "partitial ordering" von zwei Funktionstemplates von ihrer Verwendung abhängt, ist das Wort "unused" in "The presence of unused ellipsis and default arguments has no effect on the partial ordering of function templates". Habe ich irgendwas im Standard übersehen?
-
Ja, das zweite Template ist stärker spezialisiert. Allerdings werden Default-Parameter bei der Auflösung des Ziel-Templates nicht berücksichtigt - und wenn du den zweiten Parameter weglässt, bleibt von beiden Funktionen nur "f(int const&);" übrig.
Aber das steht doch auch in dem Satz, den du dort zitiert hast:
The presence of unused ellipsis and default arguments has no effect on the partial ordering of function templates
-
@igna
die partielle Ordnung wird nur auf die "viable functions" angewendet, also auf die Funktionen, die potentiell aufrufbar sind.
Default-Argumente und Ellipsen werden dabei Ordnung ignoriert. Gedanklich kannst du sie also aus den Parameterlisten rausstreichen.Gegeben deine zwei Funktionen:
template <typename T> void f(T const&, T* = 0) {} template <typename T> void f(T const&, T** = 0) {}Für den Aufruf:
f(42);sind beide Überladungen aufrufbar. Es wird also der Algorithmus für die partielle Ordnung angeworfen. Der kann aber keinen eindeutigen Kandidaten finden, denn nach der Streichung der Default-Parameter hast du:
template <typename T> void f(T const&) {} template <typename T> void f(T const&) {}Hier ist natürlich keine Funktion spezialisierter als die andere. Der Aufruf ist mehrdeutig.
Für den Aufruf:
f(42, (int**)0);hingegen gibt es überhaupt nur eine Kandidatenfunktion. Der Grund: für die erste Funktion gibt es keine Ersetzung von T, die zu den Argumenten kompatibel ist. 42 hat den Typ int. Setzt man T = int, ist der zweite Parameter vom Typ int*. Das zweite Argument hat aber den Typ int**, was nicht kompatibel zu int* ist. Andersherum, setzt man T auf int*, dann passt zwar der zweite Parameter, nicht aber der erste.
Da es nur eine Kandidatenfunktion gibt, findet hier gar keine partielle Ordnung statt. In diesem Fall spielt es also überhaupt keine Rolle, welche Funktion spezieller ist.
-
CStoll schrieb:
Ja, das zweite Template ist stärker spezialisiert.
Findest du nicht, dass das zweite Template im Falle vom Aufruf f(i, (int
0)* stärker spezialisiert ist, und im Falle vom Aufruf f(i) nicht?CStoll schrieb:
Aber das steht doch auch in dem Satz, den du dort zitiert hast:
The presence of unused ellipsis and default arguments has no effect on the partial ordering of function templates
Ja, ich habe den Satz gelesen. Nur kompiliert Comeau C/C++ 4.3.3 folgendes:
template <typename T> void f(T const&) {} template <typename T> void f(T const&, typename T::some_type* = 0) {} struct X { typedef void some_type; }; int main() { X x; f(x); // Comeau C/C++ 4.3.3 - OK // VC++ 8.0 - ambiguous call to overloaded function }Und Comeau C/C++ 4.3.3 ist der Standard-kompatibelste Compiler der Welt.

-
Und wohin wird dieser Aufruf dabei aufgelöst?
-
HumeSikkins schrieb:
die partielle Ordnung wird nur auf die "viable functions" angewendet, also auf die Funktionen, die potentiell aufrufbar sind.
Ja, anscheinend ist es so. Nur heisst der Paragraf 14.5.5.2 "Partial ordering of function templates" und nicht "Partial ordering of viable template functions", daher meine Frage.
HumeSikkins schrieb:
Für den Aufruf:
f(42, (int**)0);hingegen gibt es überhaupt nur eine Kandidatenfunktion.
Entschuldigung, ich sollte das Beispiel sorgfältiger konstruieren.

-
CStoll schrieb:
Und wohin wird dieser Aufruf dabei aufgelöst?
In das zweite Funktionstemplate.
Ich verwende Comeau C/C++ Online, der nicht linkt, deswegen habe ich folgendes ausprobiert:
template <typename T> void f(T const& x) { x.test; } // OK template <typename T> void f(T const& x, typename T::some_type* = 0) {} struct X { typedef void some_type; }; int main() { X x; f(x); }template <typename T> void f(T const& x) {} template <typename T> void f(T const& x, typename T::some_type* = 0) { x.test; } // error: class "X" has no member "test" struct X { typedef void some_type; }; int main() { X x; f(x); }
-
Interessant. Bei deinem neuen Beispiel wählt der Comeau-Compiler das zweite Template. Warum er das tut ist mir unklar. So wie ich die Regeln verstehe, sollte der Aufruf mehrdeutig sein.
Beide Templates sind anwendbar. Der Default-Parameter des zweiten Templates wird benutzt und ist damit für die Ordnung unerheblich. Damit sind die beiden Templates ordnungs-technisch aber identisch.
Der Parameter typename T::some_type ist außerdem ein sogenannter "non deduced context". Er wird also nicht automatisch hergeleitet sondern muss entweder explizit angegeben werden oder zu vorherhigen Auflösung passen.igna schrieb:
HumeSikkins schrieb:
die partielle Ordnung wird nur auf die "viable functions" angewendet, also auf die Funktionen, die potentiell aufrufbar sind.
Ja, anscheinend ist es so. Nur heisst der Paragraf 14.5.5.2 "Partial ordering of function templates" und nicht "Partial ordering of viable template functions", daher meine Frage.
In 14.5.5.2 steht aber auch, dass Partial ordering auf das Ergebnis von 14.8.2 angewendet wird. Partial ordering wird also verwendet, nachdem Template Argument Deduction mehr als ein Template-Deklaration für einen Aufruf gefunden hat.
-
@igna
Wenn du in deinem Beispiel einen Rückgabewert einbaust, siehst du leichter, welches Template der Comau auswählt:template <typename T> int f(T const&) {return 0;} template <typename T> void f(T const&, typename T::some_type* = 0) {} struct X { typedef void some_type; }; int main() { X x; int i = f(x); // Comeau: Error, "void" cannot be used to initialize "int" }
-
Nur weil der Comeau extrem standardkonform ist, heißt das doch noch lange nicht, dass er sich in jeder Situation richtig verhält, könnt ja auch sein, dass er hier keinen Fehler liefert durch nen Bug.
Der Standard gibt jedenfalls keine Regel an, nach welcher das eine Template spezialisierter als das andere ist.