Keine Funktionsdeklaration notwendig, wenn ein Funktionsparameter von einem Templateparameter abhängt ?
-
Schönen guten Abend.
Folgendes kompiliert, wie erwartet, nicht. Klar, g ist in f unbekannt.
class B { public: void f() { g(); } }; void g() { cout<<"huch?"<<endl; } int main() { B b; b.f(); }Wenn man aus B eine Klassen-Template macht, kompiliert der Code in VC++ aber nicht im gcc:
template<typename T> class B { public: void f() { g(); } }; void g() { cout<<"huch?"<<endl; } int main() { B<int> b; b.f(); }Der gcc sagt: "there are no arguments to 'g' that depend on a template parameter, so a declaration of 'g' must be available"
Ok, interessant. Also mache ich mal einen Parameter von g abhängig vom Template-Parameter:
template<typename T> class B { public: void f() { g(T()); } }; template<typename T> void g(T t) { cout<<"huch?"<<endl; } int main() { B<int> b; b.f(); }Und das kompiliert sowohl in VC++ also auch im gcc.
Ist das Standardkonform?
Warum funktioniert das, wenn der Compiler doch bei einfachen, Nichttemplate-Funktionen auf einen Prototypen oder vorherige Definition besteht?Ich bin über das letztere Verhalten in einem kleinen Projekt gestolpert und könnte das sehr gut gebrauchen, würde aber gerne wissen ob es dem Standard entspricht.
Danke

-
Das funktioniert, weil der Compiler sich Templatedefinitionen erst dann richtig genau anguckt, wenn sie gebraucht werden. Vorher überfliegt er sie quasi nur, ob die Grammatik stimmt.
Die genauen Vorschriften des Standards dazu müsste ich jetzt selber nachgucken, Templateverarbeitung ist jedenfalls hochkompliziert. Aber die praktisch wichtigen Regeln und welcher Compiler es wie handhabt hast du ja schon selber herausgefunden. Mach es also so, dass es auch der GCC frisst (der ist im Zweifelsfalle auch standardkonformer, wenn auch nicht immer), dann bist du auf der sicheren Seite.
-
Chesterfield schrieb:
Ok, interessant. Also mache ich mal einen Parameter von g abhängig vom Template-Parameter:
template<typename T> class B { public: void f() { g(T()); } }; template<typename T> void g(T t) { cout<<"huch?"<<endl; } int main() { B<int> b; b.f(); }Und das kompiliert sowohl in VC++ also auch im gcc.
g++-4.5.3 und 4.6.2 akzeptieren das, 4.7 steigt mit
test.cpp: In instantiation of ‘void B<T>::f() [with T = int]’:
test.cpp:23:9: required from here
test.cpp:10:9: error: ‘g’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
test.cpp:15:6: note: ‘template<class T> void g(T)’ declared here, later in the translation unitaus.
Siehe auch Änderungen in gcc 4.7 gleich der erste Punkt zu C++.Chesterfield schrieb:
Ist das Standardkonform?
Nein.
-

-
Verkürzt zusammengefasst:
Lookup für qualifizierte Bezeichner:
- an der Stelle der Definition, falls es sich um einen nicht-abhängigen Scope handelt
- an der Stelle der Instantiierung sonstbei unqualifizierten Bezeichnern:
- unqualifiziertes Lookup an der Stelle der Definition
- argument dependent lookup falls abhängiger Bezeichner (die Regeln dafür sind nicht ganz trivial; in g(T()) ist g aber abhängig)
- ein zweites argument dependent lookup an der Stelle der Instantiierung falls abhängigint hat keine assozierten Namensräume, folglich kann in diesem Falle g nicht durch ADL gefunden werden.
Wird dagegen das Beispiel mit
int main() { B<B<int>> b; b.f(); }gefüttert funkioniert alles. B ist im globalen Namensraum definiert worden, das ist folglich der assozierte Namensraum und dort kann dann auch das g gefunden werden.
-
camper schrieb:
bei unqualifizierten Bezeichnern:
- unqualifiziertes Lookup an der Stelle der Definition
- argument dependent lookup falls abhängiger Bezeichner (die Regeln dafür sind nicht ganz trivial; in g(T()) ist g aber abhängig)
- ein zweites argument dependent lookup an der Stelle der Instantiierung falls abhängigWie sieht es in folgendem Beispiel aus:
namespace X { class XT {}; } namespace Y { class YT {}; } template<typename T> class B { public: void f() { g(X::XT(), T()); } }; namespace X { void g(XT x, Y::YT y); } void foo() { B<Y> b; b.f(); }g(X::XT(), T()) ist ja grundsätzlich abhängig, wobei X::XT() natürlich bei der Definition des Template schon feststeht, nur T() noch nicht.
Guckt er dann bei der Instanzierung B<Y> nochmal im Namespace X nach? Oder guckt er bei der Definition im Namespace X, und bei der Instanzierung nur noch in Namespace Y?
-
hustbaer schrieb:
camper schrieb:
bei unqualifizierten Bezeichnern:
- unqualifiziertes Lookup an der Stelle der Definition
- argument dependent lookup falls abhängiger Bezeichner (die Regeln dafür sind nicht ganz trivial; in g(T()) ist g aber abhängig)
- ein zweites argument dependent lookup an der Stelle der Instantiierung falls abhängigWie sieht es in folgendem Beispiel aus:
namespace X { class XT {}; } namespace Y { class YT {}; } template<typename T> class B { public: void f() { g(X::XT(), T()); } }; namespace X { void g(XT x, Y::YT y); } void foo() { B<Y::YT> b; b.f(); }g(X::XT(), T()) ist ja grundsätzlich abhängig, wobei X::XT() natürlich bei der Definition des Template schon feststeht, nur T() noch nicht.
Guckt er dann bei der Instanzierung B<Y> nochmal im Namespace X nach? Oder guckt er bei der Definition im Namespace X, und bei der Instanzierung nur noch in Namespace Y?C++11 schrieb:
14.6.4.2 Candidate functions [temp.dep.candidate]
1 For a function call that depends on a template parameter, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2, 3.4.3) except that:
— For the part of the lookup using unqualified name lookup (3.4.1) or qualified name lookup (3.4.3), only function declarations from the template definition context are found.
— For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.
If the function name is an unqualified-id and the call would be ill-formed or would find a better match had the lookup within the associated namespaces considered all the function declarations with external linkage introduced in those namespaces in all translation units, not just considering those declarations found in the template definition and template instantiation contexts, then the program has undefined behavior.Es ist nicht erkennbar, dass zwischen Namensräumen die mit abhängigen Argumenten assoziiert sind und solchen, die von nicht-abhängigen Argumenten abhängen, unterschieden wird. X::g müsste hier also gefunden werden. Wichtig dabei noch der letzte Teil, der eigentlich eine ODR-artige Verletzung beschreibt (die Instantiierung zu einem späteren Zeitpunkt sollte nicht zu einem anderen Ergebnis führen):
namespace X { class XT {}; } namespace Y { class YT {}; } template<typename T> class B { public: void f() { g(X::XT(), T()); } }; namespace X { void g(XT x, Y::YT y); } void foo() { B<Y::YT> b; b.f(); } namespace Y { void g(XT x, Y::YT y); }hier resultiert undefiniertes Verhalten.
-
OK, wenn da diesbezüglich nix steht, dann sollte man davon ausgehen dass ein ganz normaler ADL gemacht wird - mit allen Parametern. Wäre anders wohl auch etwas kompliziert.
Was den letzten Teil angeht... sowas ähnliches gilt ja auch für inline Funktionen. Bloss fehlt dort IIRC der "all the function declarations with external linkage introduced (...) in all translation units" Teil.