Dependent names
Innerhalb der Definition einer Template (sowohl Klassentemplate als auch Funktionstemplate ) kann die Bedeutung einiger Konstrukte von einer Instanziierung zur anderen variieren. Insbesondere können Typen und Ausdrücke von Typen der Typtemplate-Parameter und Werten konstanter Template-Parameter abhängen.
template<typename T> struct X : B<T> // „B<T>“ ist abhängig von T { typename T::A* pa; // „T::A“ ist abhängig von T // (siehe unten für die Bedeutung dieser Verwendung von „typename“) void f(B<T>* pb) { static int i = B<T>::i; // „B<T>::i“ ist abhängig von T pb->j++; // „pb->j“ ist abhängig von T } };
Name lookup und Binding unterscheiden sich für abhängige Namen und nicht-abhängige Namen.
Bindungsregeln
Nicht-abhängige Namen werden am Punkt der Template-Definition gesucht und gebunden. Diese Bindung bleibt bestehen, selbst wenn am Punkt der Template-Instanziierung eine bessere Übereinstimmung vorhanden ist:
Wenn sich die Bedeutung eines nicht-abhängigen Namens zwischen dem Definitionskontext und dem Instanziierungspunkt einer Spezialisierung des Templates ändert, ist das Programm fehlerhaft, keine Diagnose erforderlich. Dies ist in den folgenden Situationen möglich:
- ein Typ, der in einem nicht-abhängigen Namen verwendet wird, ist unvollständig am Punkt der Definition, aber vollständig am Punkt der Instanziierung
|
(seit C++17) |
- eine Instanziierung verwendet ein Standardargument oder ein Standard-Template-Argument, das zum Zeitpunkt der Definition nicht definiert war
- ein konstanter Ausdruck zum Zeitpunkt der Instanziierung verwendet den Wert eines const-Objekts integralen oder unbegrenzten Aufzählungstyps , den Wert eines constexpr-Objekts, den Wert einer Referenz oder die Definition einer constexpr-Funktion (seit C++11) , und dieses Objekt /diese Referenz/Funktion (seit C++11) war zum Zeitpunkt der Definition nicht definiert
- das Template verwendet eine nicht-abhängige Klassen-Template-Spezialisierung oder Variablen-Template-Spezialisierung (seit C++14) zum Zeitpunkt der Instanziierung, und dieses verwendete Template wird entweder aus einer partiellen Spezialisierung instanziiert, die zum Zeitpunkt der Definition nicht definiert war, oder benennt eine explizite Spezialisierung, die zum Zeitpunkt der Definition nicht deklariert war
Die Bindung abhängiger Namen wird bis zur Namenssuche aufgeschoben.
Suchregeln
Die Suche nach einem abhängigen Namen, der in einer Template verwendet wird, wird aufgeschoben, bis die Template-Argumente bekannt sind, zu welchem Zeitpunkt
- non-ADL-Lookup prüft Funktionsdeklarationen mit externer Verknüpfung, die vom Template-Definitionskontext aus sichtbar sind
- ADL prüft Funktionsdeklarationen mit externer Verknüpfung, die entweder vom Template-Definitionskontext oder vom Template-Instantiierungskontext aus sichtbar sind
(mit anderen Worten, das Hinzufügen einer neuen Funktionsdeklaration nach der Template-Definition macht sie nicht sichtbar, außer über ADL).
Der Zweck dieser Regel ist es, gegen Verstöße gegen die ODR für Template-Instanziierungen zu schützen:
// eine externe Bibliothek namespace E { template<typename T> void writeObject(const T& t) { std::cout << "Wert = " << t << '\n'; } } // Übersetzungseinheit 1: // Programmierer 1 möchte E::writeObject für vector<int> ermöglichen namespace P1 { std::ostream& operator<<(std::ostream& os, const std::vector<int>& v) { for (int n : v) os << n << ' '; return os; } void doSomething() { std::vector<int> v; E::writeObject(v); // Fehler: findet P1::operator<< nicht } } // Übersetzungseinheit 2: // Programmierer 2 möchte E::writeObject für vector<int> ermöglichen namespace P2 { std::ostream& operator<<(std::ostream& os, const std::vector<int>& v) { for (int n : v) os << n << ':'; return os << "[]"; } void doSomethingElse() { std::vector<int> v; E::writeObject(v); // Fehler: findet P2::operator<< nicht } }
Im obigen Beispiel, wenn das nicht-ADL-Lookup für
operator<<
aus dem Instanziierungskontext erlaubt wäre, hätte die Instanziierung von
E
::
writeObject
<
vector
<
int
>>
zwei verschiedene Definitionen: eine, die
P1
::
operator
<<
verwendet, und eine, die
P2
::
operator
<<
verwendet. Eine solche ODR-Verletzung könnte vom Linker möglicherweise nicht erkannt werden, was dazu führen würde, dass in beiden Instanzen der eine oder andere Operator verwendet wird.
Damit ADL einen benutzerdefinierten Namespace untersucht, muss entweder std::vector durch eine benutzerdefinierte Klasse ersetzt werden oder sein Elementtyp muss eine benutzerdefinierte Klasse sein:
namespace P1 { // wenn C eine in dem P1-Namespace definierte Klasse ist std::ostream& operator<<(std::ostream& os, const std::vector<C>& v) { for (C n : v) os << n; return os; } void doSomething() { std::vector<C> v; E::writeObject(v); // OK: instanziiert writeObject(std::vector<P1::C>) // findet P1::operator<< via ADL } }
Hinweis: Diese Regel macht es unpraktisch, Operatoren für Standardbibliothekstypen zu überladen:
#include <iostream> #include <iterator> #include <utility> #include <vector> // Schlechte Idee: Operator im globalen Namespace, aber seine Argumente sind in std:: std::ostream& operator<<(std::ostream& os, std::pair<int, double> p) { return os << p.first << ',' << p.second; } int main() { typedef std::pair<int, double> elem_t; std::vector<elem_t> v(10); std::cout << v[0] << '\n'; // OK, gewöhnliche Suche findet ::operator<< std::copy(v.begin(), v.end(), std::ostream_iterator<elem_t>(std::cout, " ")); // Fehler: Sowohl die gewöhnliche Suche vom Punkt der Definition von // std::ostream_iterator als auch ADL werden nur den std-Namespace berücksichtigen, // und werden viele Überladungen von std::operator<< finden, sodass die Suche durchgeführt wird. // Die Überladungsauflösung wird dann fehlschlagen, operator<< für elem_t // in der durch die Suche gefundenen Menge zu finden. }
Hinweis: Eingeschränkte Suche (aber keine Bindung) von abhängigen Namen findet ebenfalls zum Zeitpunkt der Template-Definition statt, soweit erforderlich, um sie von unabhängigen Namen zu unterscheiden und auch um zu bestimmen, ob sie Mitglieder der aktuellen Instanziierung oder Mitglieder unbekannter Spezialisierung sind. Die durch diese Suche gewonnenen Informationen können zur Fehlererkennung verwendet werden, siehe unten.
Abhängige Typen
Die folgenden Typen sind dependent types :
- Template-Parameter
- ein Member einer unbekannten Spezialisierung (siehe unten)
- eine geschachtelte Klasse/Enum, die ein abhängiges Member einer unbekannten Spezialisierung ist (siehe unten)
- eine cv-qualifizierte Version eines abhängigen Typs
- ein zusammengesetzter Typ, der aus einem abhängigen Typ konstruiert wurde
- ein Array-Typ, dessen Elementtyp abhängig ist oder dessen Grenze (falls vorhanden) wertabhängig ist
|
(since C++11) |
- ein Funktionstyp, dessen Ausnahmespezifikation wertabhängig ist
- eine template-id bei der entweder
-
- der Template-Name ist ein Template-Parameter, oder
- einer der Template-Argumente ist typabhängig oder wertabhängig , oder ist eine Pack-Erweiterung (seit C++11) (selbst wenn die Template-ID ohne ihre Argumentliste verwendet wird, als injected-class-name )
Das Ergebnis von decltype angewendet auf einen typabhängigen Ausdruck ist ein eindeutiger abhängiger Typ. Zwei solcher Ergebnisse beziehen sich nur dann auf denselben Typ, wenn ihre Ausdrücke äquivalent sind. |
(seit C++11) |
Der Pack-Indexierungs-Spezifizierer angewendet auf einen typabhängigen konstanten Ausdruck ist ein eindeutiger abhängiger Typ. Zwei solche Pack-Indexierungs-Spezifizierer beziehen sich nur dann auf denselben Typ, wenn ihre konstanten Ausdrücke äquivalent sind. Andernfalls beziehen sich zwei solche Pack-Indexierungs-Spezifizierer nur dann auf denselben Typ, wenn ihre Indizes denselben Wert haben. |
(since C++26) |
Hinweis: Ein typedef-Mitglied einer aktuellen Instanziierung ist nur abhängig, wenn der Typ, auf den es verweist, es ist.
Typabhängige Ausdrücke
Die folgenden Ausdrücke sind type-dependent :
- ein Ausdruck, dessen beliebiger Teilausdruck ein typabhängiger Ausdruck ist
- this , wenn die Klasse ein abhängiger Typ ist.
- ein Identifikatorausdruck , der kein Konzept-ID ist und (seit C++20)
-
- enthält einen Bezeichner, für den die Namenssuche mindestens eine abhängige Deklaration findet
- enthält eine abhängige template-id
|
(seit C++11) |
-
- enthält den Namen der Konvertierungsfunktion zu einem abhängigen Typ
- enthält einen geschachtelten Namensspezifizierer oder eine qualified-id , die ein Member einer unbekannten Spezialisierung ist
- benennt ein abhängiges Member der aktuellen Instanziierung, das ein statisches Datenelement vom Typ "Array unbekannter Größe" ist
|
(seit C++14) |
|
(seit C++17) |
|
(seit C++26) |
- jede Cast-Expression auf einen abhängigen Typ
- new Expression , die ein Objekt eines abhängigen Typs erzeugt
- Member-Zugriffsexpression, die auf ein Member der aktuellen Instanziierung verweist, dessen Typ abhängig ist
- Member-Zugriffsexpression, die auf ein Member einer unbekannten Spezialisierung verweist
| (seit C++17) |
|
(seit C++26) |
Die folgenden Ausdrücke sind niemals typabhängig, weil die Typen dieser Ausdrücke nicht sein können:
| (seit C++11) |
| (seit C++20) |
Wertabhängige Ausdrücke
Die folgenden Ausdrücke sind value-dependent :
- ein Ausdruck, der in einem Kontext verwendet wird, in dem ein konstanter Ausdruck erforderlich ist, und dessen beliebiger Teilausdruck wertabhängig ist
- ein Identifikatorausdruck der eine der folgenden Bedingungen erfüllt:
|
(seit C++20) |
-
- Es ist typabhängig.
- Es ist der Name eines konstanten Template-Parameters.
- Es benennt ein statisches Datenelement, das ein abhängiges Element der aktuellen Instanziierung ist und nicht initialisiert wird.
- Es benennt eine statische Memberfunktion, die ein abhängiges Element der aktuellen Instanziierung ist.
- Es ist eine Konstante mit einem Integer- oder Aufzählungstyp (bis C++11) Literaltyp (seit C++11) , die von einem wertabhängigen Ausdruck initialisiert wird.
- die folgenden Ausdrücke, bei denen der Operand ein typabhängiger Ausdruck ist:
| (seit C++11) |
- die folgenden Ausdrücke, bei denen der Operand ein abhängiger Typ-Bezeichner ist:
- die folgenden Ausdrücke, bei denen der Zieltyp abhängig ist oder der Operand ein typabhängiger Ausdruck ist:
- function-style cast Ausdruck, bei dem der Zieltyp abhängig ist oder ein wertabhängiger Ausdruck in Klammern eingeschlossen ist oder geschweifte Klammern (seit C++11)
|
(seit C++11) |
| (seit C++17) |
- Address-of-Ausdruck, bei dem das Argument ein qualifizierter Bezeichner ist, der ein abhängiges Element der aktuellen Instanziierung benennt
- Address-of-Ausdruck, bei dem das Argument ein beliebiger Ausdruck ist, der, ausgewertet als Kern- konstanter Ausdruck , auf eine templatisierte Entität verweist, die ein Objekt mit statischer oder Thread-Speicherdauer (seit C++11) oder eine Elementfunktion ist.
Abhängige Namen
|
Dieser Abschnitt ist unvollständig
Grund: die Einleitung aus [temp.dep], die fehlt (Identifier-Ausdruck gefolgt von parenthesized list... |
|
Dieser Abschnitt ist unvollständig
Grund: Umformulierung zur möglichen Verbesserung der Klarheit (oder zumindest zur Reduzierung der Einschüchterung), und dabei CWG issue 591 anwenden |
Aktuelle Instanziierung
Innerhalb einer Klassentemplate-Definition (einschließlich ihrer Memberfunktionen und geschachtelten Klassen) können einige Namen als Bezug auf die aktuelle Instanziierung abgeleitet werden. Dies ermöglicht die Erkennung bestimmter Fehler bereits am Definitionspunkt statt bei der Instanziierung und entfernt die Anforderung an die typename - und template -Disambiguatoren für abhängige Namen, siehe unten.
Nur die folgenden Namen können auf die aktuelle Instanziierung verweisen:
-
in der Definition eines Klassentemplates, einer geschachtelten Klasse eines Klassentemplates, eines Members eines Klassentemplates oder eines Members einer geschachtelten Klasse eines Klassentemplates:
- der injizierte Klassenname des Klassentemplates oder der geschachtelten Klasse
-
in der Definition eines primären Klassentemplates oder eines Members eines primären Klassentemplates:
- der Name des Klassentemplates gefolgt von einer Template-Argumentliste (oder einer äquivalenten Alias-Template-Spezialisierung) für das primäre Template, wobei jedes Argument äquivalent (wie unten definiert) zu seinem entsprechenden Parameter ist.
-
in der Definition einer geschachtelten Klasse eines Klassentemplates:
- der Name der geschachtelten Klasse, verwendet als Member der aktuellen Instanziierung
-
in der Definition einer Klassentemplate-Partialspezialisierung oder eines Members einer Klassentemplate-Partialspezialisierung:
- der Name des Klassentemplates gefolgt von der Template-Argumentliste für die Partialspezialisierung, wobei jedes Argument äquivalent zu seinem entsprechenden Parameter ist
-
in der Definition einer
templatisierten Funktion
:
- der Name einer lokalen Klasse
Ein Template-Argument entspricht einem Template-Parameter, wenn
- für einen Typ-Parameter , bezeichnet das Template-Argument denselben Typ wie der Template-Parameter.
- für einen Konstanten-Parameter , ist das Template-Argument ein Bezeichner , der eine Variable benennt, die dem Template-Parameter entspricht. Eine Variable entspricht einem Template-Parameter, wenn
-
- es hat denselben Typ wie der Template-Parameter (unter Ignorierung von CV-Qualifizierern) und
- sein Initialisierer besteht aus einem einzelnen Bezeichner, der den Template-Parameter benennt oder, rekursiv, eine solche Variable.
template<class T> class A { A* p1; // A ist die aktuelle Instanziierung A<T>* p2; // A<T> ist die aktuelle Instanziierung ::A<T>* p4; // ::A<T> ist die aktuelle Instanziierung A<T*> p3; // A<T*> ist nicht die aktuelle Instanziierung class B { B* p1; // B ist die aktuelle Instanziierung A<T>::B* p2; // A<T>::B ist die aktuelle Instanziierung typename A<T*>::B* p3; // A<T*>::B ist nicht die aktuelle Instanziierung }; }; template<class T> class A<T*> { A<T*>* p1; // A<T*> ist die aktuelle Instanziierung A<T>* p2; // A<T> ist nicht die aktuelle Instanziierung }; template<int I> struct B { static const int my_I = I; static const int my_I2 = I + 0; static const int my_I3 = my_I; static const long my_I4 = I; static const int my_I5 = (I); B<my_I>* b1; // B<my_I> ist die aktuelle Instanziierung: // my_I hat denselben Typ wie I, // und es wird nur mit I initialisiert B<my_I2>* b2; // B<my_I2> ist nicht die aktuelle Instanziierung: // I + 0 ist kein einzelner Bezeichner B<my_I3>* b3; // B<my_I3> ist die aktuelle Instanziierung: // my_I3 hat denselben Typ wie I, // und es wird nur mit my_I (was I entspricht) initialisiert B<my_I4>* b4; // B<my_I4> ist nicht die aktuelle Instanziierung: // der Typ von my_I4 (long) ist nicht derselbe wie der Typ von I (int) B<my_I5>* b5; // B<my_I5> ist nicht die aktuelle Instanziierung: // (I) ist kein einzelner Bezeichner };
Beachten Sie, dass eine Basisklasse die aktuelle Instanziierung sein kann, wenn eine geschachtelte Klasse von ihrer umschließenden Klassenvorlage abgeleitet wird. Basisklassen, die abhängige Typen sind, aber nicht die aktuelle Instanziierung darstellen, werden als abhängige Basisklassen bezeichnet:
template<class T> struct A { typedef int M; struct B { typedef void M; struct C; }; }; template<class T> struct A<T>::B::C : A<T> { M m; // OK, A<T>::M };
Ein Name wird als Mitglied der aktuellen Instanziierung klassifiziert, wenn er
- ein nicht qualifizierter Name, der durch unqualified lookup in der aktuellen Instanziierung oder in ihrer nicht-abhängigen Basis gefunden wird.
-
qualified name
, wenn der Qualifizierer (der Name links von
::) die aktuelle Instanziierung benennt und die Suche den Namen in der aktuellen Instanziierung oder in ihrer nicht-abhängigen Basis findet - ein Name, der in einem Klassenmitglied-Zugriffsausdruck verwendet wird ( y in x. y oder xp - > y ), wobei der Objektausdruck ( x oder * xp ) die aktuelle Instanziierung ist und die Suche den Namen in der aktuellen Instanziierung oder in ihrer nicht-abhängigen Basis findet
template<class T> class A { static const int i = 5; int n1[i]; // i bezieht sich auf ein Mitglied der aktuellen Instanziierung int n2[A::i]; // A::i bezieht sich auf ein Mitglied der aktuellen Instanziierung int n3[A<T>::i]; // A<T>::i bezieht sich auf ein Mitglied der aktuellen Instanziierung int f(); }; template<class T> int A<T>::f() { return i; // i bezieht sich auf ein Mitglied der aktuellen Instanziierung }
Mitglieder der aktuellen Instanziierung können sowohl abhängig als auch unabhängig sein.
Wenn die Suche nach einem Mitglied der aktuellen Instanziierung ein anderes Ergebnis zwischen dem Instanziierungspunkt und dem Definitionspunkt liefert, ist die Suche mehrdeutig. Beachten Sie jedoch, dass wenn ein Mitgliedsname verwendet wird, dieser nicht automatisch in einen Klassenmitglied-Zugriffsausdruck umgewandelt wird; nur explizite Mitgliedszugriffsausdrücke zeigen Mitglieder der aktuellen Instanziierung an:
struct A { int m; }; struct B { int m; }; template<typename T> struct C : A, T { int f() { return this->m; } // findet A::m im Template-Definitionskontext int g() { return m; } // findet A::m im Template-Definitionskontext }; template int C<B>::f(); // Fehler: findet sowohl A::m als auch B::m template int C<B>::g(); // OK: Transformation zur Klassenmitglied-Zugriffssyntax // erfolgt nicht im Template-Definitionskontext
Unbekannte Spezialisierungen
Innerhalb einer Template-Definition werden bestimmte Namen als zu einer unbekannten Spezialisierung gehörig abgeleitet, insbesondere,
-
ein
qualifizierter Name
, falls ein Name links von
::ein abhängiger Typ ist, der kein Mitglied der aktuellen Instanziierung ist - ein qualifizierter Name , dessen Qualifizierer die aktuelle Instanziierung ist und der Name nicht in der aktuellen Instanziierung oder einer ihrer nicht-abhängigen Basisklassen gefunden wird, und eine abhängige Basisklasse existiert
- ein Name eines Members in einem Klassen-Member-Zugriffsausdruck (das y in x. y oder xp - > y ), wenn der Typ des Objektausdrucks ( x oder * xp ) ein abhängiger Typ ist und nicht die aktuelle Instanziierung darstellt
- ein Name eines Members in einem Klassen-Member-Zugriffsausdruck (das y in x. y oder xp - > y ), wenn der Typ des Objektausdrucks ( x oder * xp ) die aktuelle Instanziierung ist und der Name nicht in der aktuellen Instanziierung oder einer ihrer nicht-abhängigen Basisklassen gefunden wird, und eine abhängige Basisklasse existiert
template<typename T> struct Base {}; template<typename T> struct Derived : Base<T> { void f() { // Derived<T> bezieht sich auf die aktuelle Instanziierung // in der aktuellen Instanziierung existiert kein "unknown_type" // aber es gibt eine abhängige Basisklasse (Base<T>) // Daher ist "unknown_type" ein Member einer unbekannten Spezialisierung typename Derived<T>::unknown_type z; } }; template<> struct Base<int> // diese Spezialisierung stellt es bereit { typedef int unknown_type; };
Diese Klassifizierung ermöglicht es, die folgenden Fehler bereits an der Stelle der Template-Definition (anstatt der Instanziierung) zu erkennen:
- Falls eine Template-Definition einen qualifizierten Namen besitzt, bei dem der Qualifizierer auf die aktuelle Instanziierung verweist und der Name weder ein Member der aktuellen Instanziierung noch ein Member einer unbekannten Spezialisierung ist, ist das Programm fehlerhaft (keine Diagnose erforderlich), selbst wenn das Template niemals instanziiert wird.
template<class T> class A { typedef int type; void f() { A<T>::type i; // OK: „type“ ist ein Member der aktuellen Instanziierung typename A<T>::other j; // Fehler: // „other“ ist kein Member der aktuellen Instanziierung // und auch kein Member einer unbekannten Spezialisierung, // da A<T> (was die aktuelle Instanziierung benennt) // keine abhängigen Basenklassen besitzt, in denen sich „other“ verbergen könnte. } };
- Wenn eine Template-Definition einen Member-Zugriffsausdruck enthält, bei dem der Objektausdruck die aktuelle Instanziierung ist, der Name jedoch weder ein Member der aktuellen Instanziierung noch ein Member einer unbekannten Spezialisierung ist, ist das Programm ill-formed, selbst wenn das Template niemals instanziiert wird.
Mitglieder von unbekannter Spezialisierung sind immer abhängig und werden am Punkt der Instanziierung gesucht und gebunden wie alle abhängigen Namen (siehe oben)
Der typename -Disambiguierer für abhängige Namen
In einer Deklaration oder Definition eines Templates, einschließlich Alias-Templates, wird ein Name, der kein Member der aktuellen Instanziierung ist und von einem Template-Parameter abhängt, nicht als Typ betrachtet, es sei denn, das Schlüsselwort typename wird verwendet oder es wurde bereits als Typname etabliert, z.B. durch eine typedef-Deklaration oder durch die Verwendung zur Benennung einer Basisklasse.
#include <iostream> #include <vector> int p = 1; template<typename T> void foo(const std::vector<T> &v) { // std::vector<T>::const_iterator ist ein abhängiger Name, typename std::vector<T>::const_iterator it = v.begin(); // ohne "typename" wird das Folgende als Multiplikation // des typabhängigen Datenelements "const_iterator" // und einer Variable "p" geparst. Da an dieser Stelle // ein globales "p" sichtbar ist, kompiliert diese Template-Definition. std::vector<T>::const_iterator* p; typedef typename std::vector<T>::const_iterator iter_t; iter_t * p2; // "iter_t" ist ein abhängiger Name, aber bekannt als Typname } template<typename T> struct S { typedef int value_t; // Mitglied der aktuellen Instanziierung void f() { S<T>::value_t n{}; // S<T> ist abhängig, aber "typename" nicht benötigt std::cout << n << '\n'; } }; int main() { std::vector<int> v; foo(v); // Template-Instanziierung schlägt fehl: Es gibt keine Member-Variable // namens "const_iterator" im Typ std::vector<int> S<int>().f(); }
Das Schlüsselwort typename darf nur auf diese Weise vor qualifizierten Namen verwendet werden (z.B. T :: x ), aber die Namen müssen nicht abhängig sein.
Übliche qualifizierte Namenssuche wird für den mit typename präfixierten Bezeichner verwendet. Im Gegensatz zum Fall mit elaborated type specifier ändern sich die Suchregeln trotz des Qualifizierers nicht:
struct A // A hat eine geschachtelte Variable X und einen geschachtelten Typ struct X { struct X {}; int X; }; struct B { struct X {}; // B hat einen geschachtelten Typ struct X }; template<class T> void f(T t) { typename T::X x; } void foo() { A a; B b; f(b); // OK: instanziiert f<B>, T::X bezieht sich auf B::X f(a); // Fehler: kann f<A> nicht instanziieren: // weil qualifizierte Namenssuche für A::X das Datenelement findet }
Das Schlüsselwort typename kann auch außerhalb von Templates verwendet werden.
#include <vector> int main() { // Beide OK (nach Lösung von CWG 382) typedef typename std::vector<int>::const_iterator iter_t; typename std::vector<int> v; }
|
In manchen Kontexten können nur Typnamen gültig erscheinen. In diesen Kontexten wird angenommen, dass ein abhängiger qualifizierter Name einen Typ benennt und kein typename erforderlich ist:
|
(seit C++20) |
Der template Disambiguator für abhängige Namen
Ebenso wird in einer Template-Definition ein abhängiger Name, der kein Member der aktuellen Instanziierung ist, nicht als Template-Name betrachtet, sofern nicht das Disambiguierungsschlüsselwort template verwendet wird oder es nicht bereits als Template-Name festgelegt wurde:
template<typename T> struct S { template<typename U> void foo() {} }; template<typename T> void bar() { S<T> s; s.foo<T>(); // Fehler: < als kleiner-als-Operator interpretiert s.template foo<T>(); // OK }
Das Schlüsselwort template darf nur auf diese Weise nach den Operatoren :: (Bereichsauflösung), - > (Memberzugriff über Zeiger) und . (Memberzugriff) verwendet werden, die folgenden sind alle gültige Beispiele:
- T :: template foo < X > ( ) ;
- s. template foo < X > ( ) ;
- this - > template foo < X > ( ) ;
- typename T :: template iterator < int > :: value_type v ;
Wie im Fall von typename ist das template -Präfix auch dann erlaubt, wenn der Name nicht abhängig ist oder die Verwendung nicht im Gültigkeitsbereich eines Templates erscheint.
Selbst wenn der Name links von
::
auf einen Namespace verweist, ist der Template-Disambiguator erlaubt:
template<typename> struct S {}; ::template S<void> q; // erlaubt, aber unnötig
|
Aufgrund der speziellen Regeln für unqualified name lookup für Template-Namen in Member-Zugriffsausdrücken ist das Disambiguierungstoken unnötig, wenn ein nicht-abhängiger Template-Name in einem Member-Zugriffsausdruck (nach - > oder nach . ) erscheint und ein Klassen- oder Alias-Template (seit C++11) mit demselben Namen durch gewöhnliche Suche im Kontext des Ausdrucks gefunden wird. Allerdings ist das Programm fehlerhaft, wenn das im Kontext des Ausdrucks gefundene Template sich von dem im Klassenkontext gefundenen unterscheidet (bis C++11) template<int> struct A { int value; }; template<class T> void f(T t) { t.A<0>::value; // Ordinary lookup of A finds a class template. // A<0>::value names member of class A<0> // t.A < 0; // Error: “<” is treated as the start of template argument list } |
(bis C++23) |
Schlüsselwörter
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 206 | C++98 |
es war nicht spezifiziert, zu welchem Zeitpunkt semantische Einschränkungen
angewendet werden, wenn ein Typ, der in einem nicht-abhängigen Namen verwendet wird, an der Stelle, an der ein Template definiert wird, unvollständig ist, aber an der Stelle, an der eine Instanziierung durchgeführt wird, vollständig ist |
das Programm ist fehlerhaft
und in diesem Fall ist keine Diagnose erforderlich |
| CWG 224 | C++98 |
die Definition abhängiger Typen basierte
auf der Form des Namens anstatt auf Lookup |
Definition überarbeitet |
| CWG 382 | C++98 | der typename Disambiguator war nur im Template-Bereich erlaubt |
auch außerhalb
von Templates erlaubt |
| CWG 468 | C++98 | der template Disambiguator war nur im Template-Bereich erlaubt |
auch außerhalb
von Templates erlaubt |
| CWG 502 | C++98 | es war nicht spezifiziert, ob geschachtelte Enumerationen abhängig sind | abhängig wie geschachtelte Klassen |
| CWG 1047 | C++98 | typeid Ausdrücke waren niemals wertabhängig |
wertabhängig, wenn der
Operand typabhängig ist |
| CWG 1160 | C++98 |
es war nicht spezifiziert, ob ein Name sich auf die aktuelle Instanziierung bezieht,
wenn eine Template-ID, die auf ein primäres Template oder eine partielle Spezialisierung passt, in der Definition eines Members des Templates erscheint |
spezifiziert |
| CWG 1413 | C++98 |
nicht initialisierte statische Datenelemente, statische Memberfunktionen und Adressen
von Membern einer Klassenvorlage waren nicht als wertabhängig aufgeführt |
aufgeführt |
| CWG 1471 | C++98 |
ein geschachtelter Typ einer nicht-abhängigen Basis der
aktuellen Instanziierung war abhängig |
er ist nicht abhängig |
| CWG 1850 | C++98 |
die Liste der Fälle, in denen sich die Bedeutung zwischen dem
Definitionskontext und dem Instanziierungspunkt ändern kann, war unvollständig |
vervollständigt |
| CWG 1929 | C++98 |
es war nicht klar, ob der
template
Disambiguator einem
::
folgen kann,
bei dem der Name links davon auf einen Namespace verweist |
erlaubt |
| CWG 2066 | C++98 | this war niemals wertabhängig |
es kann
wertabhängig sein |
| CWG 2100 | C++98 |
Adresse eines statischen Datenelements einer Klassen-
vorlage war nicht als wertabhängig aufgeführt |
aufgeführt |
| CWG 2109 | C++98 | typabhängige Identifikatorausdrücke könnten nicht wertabhängig sein |
sie sind immer
wertabhängig |
| CWG 2276 | C++98 |
ein Funktionstyp, dessen Ausnahmespezifikation
wertabhängig ist, war kein abhängiger Typ |
er ist es |
| CWG 2307 | C++98 |
ein in Klammern gesetzter konstanter Template-Parameter, der als
Template-Argument verwendet wird, war äquivalent zu diesem Template-Parameter |
nicht mehr äquivalent |
| CWG 2457 | C++11 |
ein Funktionstyp mit Funktionsparameter-
Pack war kein abhängiger Typ |
er ist es |
| CWG 2785 | C++20 | requires Ausdrücke könnten typabhängig sein |
sie sind niemals
typabhängig |
| CWG 2905 | C++11 |
ein
noexcept
Ausdruck war nur wertabhängig,
wenn sein Operand wertabhängig ist |
er ist wertabhängig,
wenn sein Operand einen Template-Parameter beinhaltet |
| CWG 2936 | C++98 |
die Namen lokaler Klassen von templatisierten
Funktionen waren nicht Teil der aktuellen Instanziierung |
sie sind es |