Problem mit Funktionszeiger und ?: und std::min
-
Was vielleicht noch interessant zu wissen ist: G++ vor 4.5 bekommt das auch mit meinTMax/meinTMin nicht aufgelöst. Der Intel Compiler schluckt alles (auch den Problemcode) ohne zu mucken.
-
SeppJ schrieb:
Was vielleicht noch interessant zu wissen ist: G++ vor 4.5 bekommt das auch mit meinTMax/meinTMin nicht aufgelöst. Der Intel Compiler schluckt alles (auch den Problemcode) ohne zu mucken.
Hm, kommt mir auch komisch vor. Wenn ich mir §13.4 ansehe würde ich sagen der "Problemcode" ist schlicht nicht standardkonform und somit Murks.
Mit der expliziten Typumwandlung passt es dann wieder. Aber ohne sollte nur die Zuweisung in der if else Bedingung funktionieren.
//edit
Tippfehler von §14.3 auf §13.4 geändert
-
dann hast du ein problem mit §14.8.1/2.
aber vielleicht auch nicht.
man könnte das auch so lesen:template <int(*)(int,int)> struct S { } template <class T> void foo (T, T) { } void bar () { S<foo> s; //ok nach §13.4 &foo; //ill-formed nach §13.4 &foo<int>; //ill-formed nach §13.4, §14.8.1/2 kommt noch nicht zur anwendung, //da §13.4 nach $3.5.1 *zuerst* gegeben sein muss (*) }§14.8.1/2 wäre dann nur für so etwas gut:
template <int(*)(int,int)> struct S { } template <class T> void foo (T, T) { } template <class T, class U> void foo (T, U) { } void bar () { S<foo> s; //würde foo(T,T) nach §13.4 in kombination mit §14.5.5.2 auswählen S<foo<int, int> > t; //würde foo(T, U) auswählen }(*) die explizite template-parameter angabe müsste dafür für §13.4 irrelevant sein, ich habe jetzt aber auf die schnelle nichts gefunden, dass das explizit vorschreibt oder verbietet. vermutlich ist der ursprüngliche code also wirklich nicht standardkonform und dass gcc beim eigenen template meinTMin den code durchgehen lässt falsch.
aber vielleicht kann sich ja wer mit mehr kombinationsgabe dazu äußern.
-
danke für die Erklärungsversuche. Ich dachte bisher, ?: könnte man bei einer Zuweisung als Abkürzung für if/else gebrauchen. Das scheint aber nicht immer der Fall zu sein. Unter welchen Bedingungen, müsste einem normal-User mal in klarem Text gesagt werden. Stroustrup (Programming Principles and Practise using C++) schreibt auch nur schlicht "The value of (a>=b)?a:b is a if a>=b and b otherwise." (S. 266) Das erklärt den Unterschied nicht.
und was genau ist an dem if-Statement nicht standardkonform, wie vermutet?
Die andere Frage: Mit dem static_cast funktioniert es, aber warum? Denn da wird der Funktionszeiger doch in genau denselben Typ umgewandelt, den er eh schon hat:
template<class T> const T& min(const T& a, const T& b);siehe 25.4.7 StdEntwurf
-
RudiRatlos2 schrieb:
danke für die Erklärungsversuche. Ich dachte bisher, ?: könnte man bei einer Zuweisung als Abkürzung für if/else gebrauchen. Das scheint aber nicht immer der Fall zu sein. Unter welchen Bedingungen, müsste einem normal-User mal in klarem Text gesagt werden. Stroustrup (Programming Principles and Practise using C++) schreibt auch nur schlicht "The value of (a>=b)?a:b is a if a>=b and b otherwise." (S. 266) Das erklärt den Unterschied nicht.
und was genau ist an dem if-Statement nicht standardkonform, wie vermutet?
Die andere Frage: Mit dem static_cast funktioniert es, aber warum? Denn da wird der Funktionszeiger doch in genau denselben Typ umgewandelt, den er eh schon hat:
template<class T> const T& min(const T& a, const T& b);siehe 25.4.7 StdEntwurf
Das Problem ist, dass min noch eine weitere Überladung besitzt:
template<class T, class Compare> const T& min(const T& a, const T& b, Compare comp);Aus diesem Grunde bezeichnet min<int> nicht nur eine einzige eindeutige Templatespezialisierung.
Mithin kann der Typ des 2. und 3. Operanden des ternären Operators nicht bestimmt werden.
Umgekehrt ist die Variante per if eindeutig, weil in den beiden Zweigen jeweils eine einfache Zuweisung stattfindet. Für diesen Fall existiert eine Ausnahme in 13.4/1, die es erlaubt, die korrekte Überladung anhand des Typs der zugewiesenen Variablen zu bestimmen.
-
Jetzt kommen wir der Sache näher.
13.4/1 (Dokument N3290) lautet (Auszug): "A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type is identical to the function type of the target type required in the context."und
"The target can be
[...]
— the left side of an assignment (5.17),"Mit anderen Worten:
Der G++Compiler ignoriert den target type im ?:-Fall. Dabei ist der Typ des Targets ("required in the context"!) eindeutig spezifiziert, genau wie bei der einfachen Zuweisung. Mir scheint, dass der Intel-Compiler an der Stelle akkurater ist als der G++.
-
RudiRatlos2 schrieb:
Dabei ist der Typ des Targets ("required in the context"!) eindeutig spezifiziert, genau wie bei der einfachen Zuweisung.
Keineswegs, denn Kontext meint nicht irgendeine Zuweisung, die Teil des vollständigen Ausrucks ist, sondern bezeichnet immer das unmittelbar umschließende Konstrukt, für das die Template-id Operatorand ist, in diesem Fall also conditional-expression. Operand der Zuweisung ist nicht mehr der mehrdeutige Nahme sondern eben dieser bedingte Ausdruck.
-
davie schrieb:
...(*) die explizite template-parameter angabe müsste dafür für §13.4 irrelevant sein, ich habe jetzt aber auf die schnelle nichts gefunden, dass das explizit vorschreibt oder verbietet....
Da liegt auch für mich der Knackpunkt. Wenn man die Definition von "argument" zugrunde legt, trifft §13.4 auf den Code des Threadstartes überhaupt nicht zu und die Sache wird kompliziert.
@camper
Istfp = even ? meinTMax<int> : meinTMin<int>;eigentlich in Ordnung?
-
Berechtigte Frage! Da trifft Campers Argument mit der weiteren überladenen Funktion auch nicht zu. Auch ist "context" im Standardentwurf an keiner Stelle so definiert.
Merkwürdigerweise wird der Ausdruck bei mir (Linux G++4.5.2. und 4.7 (experimental)) compiliert, gibt aber einen Link-Fehler:tmp/ccpjkPlf.o: In function `main': stdmin.cpp:(.text+0x26): undefined reference to `int const& meinTMax<int>(int const&, int const&)' stdmin.cpp:(.text+0x2d): undefined reference to `int const& meinTMin<int>(int const&, int const&)'Es muss also doch etwas mit der Typevaluation zu tun haben.
(Dummerweise hatte ich das wegen der if-Anweisung, die eine korrekte Implementierung der Funktion erzeugt, nicht gemerkt. Die if-Anweisung muss gelöscht werden, um diesen Effekt zu sehen)
-
Mitleid schrieb:
@camper
Istfp = even ? meinTMax<int> : meinTMin<int>;eigentlich in Ordnung?
Vermutlich...
Interessanterweise wird der Code OP von g++ 4.5.2 (gentoo) in Gänze geschluckt mit -std=c++98; mit -std=c++0x wird dagegen die angemerkte Zeile bemängelt.
Comeau hat keinerlei Einwände gegen den Code (mit und ohne C++0x-Erweiterungen).Ich behalte mir deshalb vor, mich noch nicht festzulegen.
Edit: in Hinblick auf obigen Linkerfehler vermute ich mal, dass hier ein Bug im gcc vorliegt.
-
Mitleid schrieb:
@camper
Istfp = even ? meinTMax<int> : meinTMin<int>;eigentlich in Ordnung?
In Hinblick auf C++0x müsste das zulässig sein, siehe auch mein Link weiter oben.
Die Einfügung in 14.8.1/3 im gegenwärtigen Entwurf für C++0xIn contexts where deduction is done and fails, or in contexts where deduction is not done, if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template
specialization.ist dafür ausreichend. Im Falle von C++03 gehe ich davon aus, dass hier eine Regelungslücke besteht.
fp = even ? std::max<int> : std::min<int>;ist damit ebenso nach C++0x zulässig, da die Argumentdeduktion für die 3-parametrige Funktion fehlschlägt, bleibt nur eine Funktionsspezialisierung übrig.
-
Also, ist es dann doch so, dass durch die Angabe der "template-Parameter" §13.4 in keinem der Fälle zutrifft und es eigentlich nur darum geht, dass eindeutig eine Spezialisierung ausgewählt werden kann.
Wenn man allerdings die Parameter weglässt (so wie du es vor deinem letzen Edit hattest) §13.4 dann doch voll greift und die Fälle
fp = even ? meinTMax : meinTMin; fp = even ? std::max : std::min;ausschließt, während
if(even) fp = std::max; else fp = std::min;weiterhin gültig bleiben würde.
-
Mitleid schrieb:
Also, ist es dann doch so, dass durch die Angabe der "template-Parameter" §13.4 in keinem der Fälle zutrifft und es eigentlich nur darum geht, dass eindeutig eine Spezialisierung ausgewählt werden kann.
Wenn man allerdings die Parameter weglässt (so wie du es vor deinem letzen Edit hattest) §13.4 dann doch voll greift und die Fälle
fp = even ? meinTMax : meinTMin; fp = even ? std::max : std::min;ausschließt,
Ich bin mir schon wieder nicht so sicher wegen des Edits:
In contexts where deduction is done and fails, or in contexts where deduction is not done, if a template argument list is specified and it, along with any default template arguments, identifies a single function template specialization, then the template-id is an lvalue for the function template specialization.
Denn Argumentdeduktion schlägt ja hier nicht fehl, sondern wird gar nicht ausgeführt, da das gerade kein zulässiger Fall für 13.4 ist.
Zudem kommen noch ein paar Überladungen dazu:// 25.4.7, minimum and maximum: template<class T> const T& min(const T& a, const T& b); template<class T, class Compare> const T& min(const T& a, const T& b, Compare comp); template<class T> T min(initializer_list<T> t); template<class T, class Compare> T min(initializer_list<T> t, Compare comp);Mitleid schrieb:
während
if(even) fp = std::max; else fp = std::min;weiterhin gültig bleiben würde.
Das ist auf jeden Fall zulässig und unproblematisch. Über 13.4/1 2. Alternative -> 13.4/2 -> 14.8.2.2, da dir Argumentdeduktion nur für eine der Überladungen erfolgreich ist.
-
//gelöscht
Hatte dich falsch verstanden. In Bezug auf std::max/std::min meinten wir ja dasselbe. Und
fp = even ? meinTMax : meinTMin;ist ja eigentlich auch klar. Das hat wieder nichts mit §13.4 zu tun (weil nicht überladen, behaupte ich jetzt mal dreist), sondern ist einfach mehrdeutig.
Aber gut, Rudis Frage ist ja ohnehin bereits beantwortet.