Gelegentlich habe ich einige wirklich nicht entzifferbare Fehlermeldungen gesehen, die von ausgespuckt wurden gcc bei der Verwendung von Vorlagen … Insbesondere hatte ich Probleme, bei denen scheinbar korrekte Deklarationen sehr seltsame Kompilierungsfehler verursachten, die auf magische Weise durch das Präfixieren von verschwanden typename Schlüsselwort an den Anfang der Deklaration … (Zum Beispiel habe ich erst letzte Woche zwei Iteratoren als Mitglieder einer anderen Schablonenklasse deklariert, und ich musste dies tun) …
Worum geht es in der Geschichte typename?
stackoverflow.com/questions/1600464
– sbi
21. Oktober 2009 um 17:12 Uhr
Naveen
Es folgt das Zitat aus Josuttis Buch:
Das Schlüsselwort typename wurde eingeführt, um anzugeben, dass der folgende Bezeichner ein Typ ist. Betrachten Sie das folgende Beispiel:
Hier, typename wird verwendet, um das zu verdeutlichen SubType ist eine Art von class T. Daher, ptr ist ein Zeiger auf den Typ T::SubType. Ohne typename, SubType
würde als statisches Mitglied betrachtet werden. Daher
T::SubType * ptr
wäre eine Vervielfachung des Wertes SubType des Typs T mit ptr.
Tolles Buch. Lesen Sie es einmal durch und behalten Sie es als Referenz, wenn Sie möchten.
– geschickt_code
21. Oktober 2009 um 15:46 Uhr
Der aufmerksame Leser wird erkennen, dass ein Multiplikationsausdruck von der Grammatik für eine Mitgliedsdeklaration nicht erlaubt ist. Als solches C++20 verzichtet auf die Notwendigkeit dafür typename (wenn auch nicht alle!).
– Davis Hering
9. Mai 2019 um 3:52 Uhr
Hat mich nicht überzeugt. Sobald die Vorlage instanziiert ist, ist sehr gut definiert, was der T::Subtyp ist
Ströstrup das vorhandene Schlüsselwort class wiederverwendet einen Typparameter anzugeben, anstatt ein neues Schlüsselwort einzuführen, das natürlich bestehende Programme beschädigen könnte. Es war nicht so, dass ein neues Schlüsselwort nicht in Betracht gezogen wurde – nur, dass es angesichts seiner potenziellen Störung nicht als notwendig erachtet wurde. Und Bis zum ISO-C++-Standard war dies die einzige Möglichkeit, einen Typparameter zu deklarieren.
Im Grunde genommen hat Stroustrup das Klassenschlüsselwort wiederverwendet, ohne ein neues Schlüsselwort einzuführen, das später im Standard aus den folgenden Gründen geändert wird
Sprachgrammatik missinterpretiert T::A *aObj; als arithmetischer Ausdruck wird also ein neues Schlüsselwort eingeführt genannt typename
typename T::A* a6;
es weist den Compiler an, die nachfolgende Anweisung als Deklaration zu behandeln.
Da das Schlüsselwort auf der Gehaltsliste stand, zum Teufel, warum die durch die ursprüngliche Entscheidung verursachte Verwirrung nicht beheben um das Klassenschlüsselwort wiederzuverwenden.
Deshalb haben wir beides
Kann man sich anschauen dieser Beitrages wird dir definitiv helfen, ich habe gerade so viel daraus extrahiert, wie ich konnte
Ja, aber warum war dann ein neues Schlüsselwort typename erforderlich, wenn Sie das vorhandene Schlüsselwort verwenden könnten class für den gleichen Zweck?
– Jesper
21. Oktober 2009 um 13:59 Uhr
@Jesper: Ich denke, die Antwort von Xenus ist hier verwirrend. typename wurde notwendig, um das Parsing-Problem zu beheben, wie in Naveens Antwort beschrieben, indem Josuttis zitiert wurde. (Ich glaube nicht, dass das Einfügen einer class an dieser Stelle hätte funktioniert.) Erst nachdem das neue Schlüsselwort für diesen Fall akzeptiert wurde, wurde es auch in Template-Argument-Deklarationen erlaubt (oder sind das Definitionen?), weil das class es war schon immer etwas irreführend.
Leider muss der Compiler kein Hellseher sein und weiß nicht, ob T::sometype am Ende auf einen Typnamen oder ein statisches Mitglied von T verweist. Also verwendet man typename um es zu sagen:
In einigen Situationen, in denen Sie sich auf ein Mitglied so genannter beziehen abhängig type (bedeutet “abhängig von Template-Parameter”), kann der Compiler nicht immer eindeutig auf die semantische Bedeutung des resultierenden Konstrukts schließen, weil er nicht weiß, um was für einen Namen es sich handelt (also ob es sich um einen Namen eines Typs, einen Namen handelt eines Datenelements oder Name von etwas anderem). In solchen Fällen müssen Sie die Situation eindeutig machen, indem Sie dem Compiler explizit mitteilen, dass der Name zu einem Typnamen gehört, der als Mitglied dieses abhängigen Typs definiert ist.
Zum Beispiel
template <class T> struct S {
typename T::type i;
};
In diesem Beispiel das Schlüsselwort typename erforderlich, damit der Code kompiliert werden kann.
Dasselbe passiert, wenn Sie auf ein Template-Member vom abhängigen Typ verweisen möchten, dh auf einen Namen, der ein Template bezeichnet. Sie müssen auch dem Compiler helfen, indem Sie das Schlüsselwort verwenden templateobwohl es anders platziert ist
template <class T> struct S {
T::template ptr<int> p;
};
In einigen Fällen kann es erforderlich sein, beide zu verwenden
Natürlich spielt das Schlüsselwort eine andere Rolle typename in Vorlagenparameterdeklarationen verwendet werden soll.
Das Geheimnis liegt darin, dass eine Vorlage für einige Typen spezialisiert werden kann. Das heißt, es kann auch die Schnittstelle für mehrere Typen völlig unterschiedlich definieren. Sie können zum Beispiel schreiben:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
Man könnte sich fragen, warum das sinnvoll ist und in der Tat: Das sieht wirklich nutzlos aus. Aber denken Sie zum Beispiel daran std::vector<bool> der reference Typ sieht ganz anders aus als bei anderen TS. Zugegebenermaßen ändert es nichts an der Art reference von einem Typ zu etwas anderem, aber es könnte trotzdem passieren.
Was passiert nun, wenn Sie damit Ihre eigenen Vorlagen schreiben? test Vorlage. Etwas wie das
es scheint ok für dich zu sein, weil du erwarten das test<T>::ptr ist ein Typ. Aber der Compiler weiß es nicht, und tatsächlich wird ihm vom Standard sogar geraten, das Gegenteil zu erwarten, test<T>::ptr ist kein Typ. Um dem Compiler mitzuteilen, was Sie erwarten, müssen Sie a hinzufügen typename Vor. Die richtige Vorlage sieht so aus
Fazit: Sie müssen hinzufügen typename bevor Sie einen verschachtelten Vorlagentyp in Ihren Vorlagen verwenden. (Natürlich nur, wenn ein Template-Parameter Ihres Templates für dieses innere Template verwendet wird.)
NAND
Zwei Verwendungen:
Als ein template Argument-Schlüsselwort (anstelle von class)
EIN typename Das Schlüsselwort teilt dem Compiler mit, dass ein Bezeichner ein Typ ist (und keine statische Elementvariable).
template <typename T> class X // [1]
{
typename T::Y _member; // [2]
}
Ich denke, alle Antworten haben erwähnt, dass die typename Schlüsselwort, wird in zwei verschiedenen Fällen verwendet:
a) Beim Deklarieren eines Vorlagentypparameters. z.B
template<class T> class MyClass{}; // these two cases are
template<typename T> class MyNewClass{}; // exactly the same.
Wobei es keinen Unterschied zwischen ihnen gibt und sie GENAU gleich sind.
b) Vor der Verwendung von a Name des verschachtelten abhängigen Typs für eine Vorlage.
template<class T>
void foo(const T & param)
{
typename T::NestedType * value; // we should use typename here
}
Welche nicht verwenden typename führt zu Analyse-/Kompilierungsfehlern.
Was ich dem zweiten Fall hinzufügen möchte, wie er in Scot Meyers Buch Effective C++ erwähnt wird, ist, dass es eine Ausnahme von using gibt typename vor einem Name des verschachtelten abhängigen Typs. Die Ausnahme ist, wenn Sie die verwenden Name des verschachtelten abhängigen Typs entweder als Basisklasse oder in einem Member-Initialisierungslistesollten Sie nicht verwenden typename dort:
template<class T>
class D : public B<T>::NestedType // No need for typename here
{
public:
D(std::string str) : B<T>::NestedType(str) // No need for typename here
{
typename B<T>::AnotherNestedType * x; // typename is needed here
}
}
Notiz: Verwenden typename für den zweiten Fall (dh vor dem verschachtelten abhängigen Typnamen) wird seit C++20 nicht mehr benötigt.
9879800cookie-checkWofür steht der Typname offiziell?yes
stackoverflow.com/questions/1600464
– sbi
21. Oktober 2009 um 17:12 Uhr