
dicroce
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
?

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:
template <class T>
Class MyClass
{
typename T::SubType * ptr;
...
};
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
.

Xinus
Stan Lippmans BLog-Beitrag schlägt vor :-
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
Wie das gegebene Beispiel
template <class T>
class Demonstration {
public:
void method() {
T::A *aObj; // oops …
// …
};
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
Betrachten Sie den Code
template<class T> somefunction( T * arg )
{
T::sometype x; // broken
.
.
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:
template<class T> somefunction( T * arg )
{
typename T::sometype x; // works!
.
.

Ameise
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 template
obwohl 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
template <class T> struct S {
typename T::template ptr<int>::type i;
};
(wenn ich die Syntax richtig verstanden habe).
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 T
S. 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
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
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
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
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