Namespaces
Variants

Dependent names

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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.

Inhaltsverzeichnis

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:

#include <iostream>
void g(double) { std::cout << "g(double)\n"; }
template<class T>
struct S
{
    void f() const
    {
        g(1); // "g" ist ein unabhängiger Name, jetzt gebunden
    }
};
void g(int) { std::cout << "g(int)\n"; }
int main()
{
    g(1);  // ruft g(int) auf
    S<int> s;
    s.f(); // ruft g(double) auf
}

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
  • Die Suche nach einem Namen in der Template-Definition fand eine using-declaration , aber die Suche im entsprechenden Scope bei der Instanziierung findet keine Deklarationen, weil die using-declaration eine Pack-Expansion war und das entsprechende Pack leer ist
(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

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
  • enthält den speziellen Bezeichner __func__ (falls eine umschließende Funktion ein Template ist, ein Nicht-Template-Member eines Klassentemplates oder ein generisches Lambda (seit C++14) )
(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
  • enthält einen Bezeichner, für den die Namenssuche eine oder mehrere Deklarationen von Memberfunktionen der aktuellen Instanziierung findet, die mit Rückgabetypableitung deklariert wurden
(seit C++14)
  • enthält einen Bezeichner, für den die Namenssuche eine strukturierte Bindungsdeklaration findet, deren Initialisierer typabhängig ist
  • enthält einen Bezeichner, für den die Namenssuche einen konstanten Template-Parameter findet, dessen Typ den Platzhalter auto enthält
  • enthält einen Bezeichner, für den die Namenssuche eine Variable findet, die mit einem Typ deklariert wurde, der einen Platzhaltertyp enthält (z.B. auto statisches Datenelement), wobei der Initialisierer typabhängig ist
(seit C++17)
  • enthält einen Bezeichner, für den die Namenssuche ein Pack findet
(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:
  • Es handelt sich um eine concept-id und eines ihrer Argumente ist abhängig.
(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

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 :

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:

  • Ein qualifizierter Name, der in Typ-ID erscheint, wobei die kleinste umschließende Typ-ID 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 ;
*Hinweis: Da der gesamte Inhalt innerhalb von ` ` Tags liegt (was als Codebereich betrachtet wird) und gemäß den Anweisungen Code nicht übersetzt werden soll, bleibt der gesamte Text unverändert.*

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

template , typename

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