2009-10-21 7 views
79

Gelegentlich habe ich einige wirklich nicht entzifferbare Fehlermeldungen gesehen, die gcc bei der Verwendung von Templates ausspuckt ... Konkret hatte ich Probleme, wo scheinbar korrekte Deklarationen sehr seltsame Kompilierungsfehler verursachten, die auf magische Weise durch den Präfix "typename" verschwanden. Schlüsselwort an den Anfang der Deklaration ... (Zum Beispiel habe ich erst letzte Woche zwei Iteratoren als Mitglieder einer anderen Templates-Klasse deklariert und ich musste das tun) ...Offiziell, was ist Typname für?

Was ist die Geschichte über Typname?

+3

http://stackoverflow.com/questions/1600464/ – sbi

Antwort

124

Es folgt das Zitat aus Josuttis Buch :

das Schlüsselwort Typname auf eingeführt wurde festzulegen, dass der Identifikator, der einen Typ folgt, ist. Betrachten Sie das folgende Beispiel :

template <class T> 
Class MyClass 
{ 
    typename T::SubType * ptr; 
    ... 
}; 

Hier Typname verwendet wird, um klarzustellen, dass SubType eine Art von Klasse T. ist Somit ist ptr ein Zeiger auf den Typ T :: SubType. Ohne Typname würde SubType als ein statisches Mitglied betrachtet werden. So

T::SubType * ptr 

würde eine Vervielfachung der Wert SubType vom Typ T mit ptr sein.

+1

Tolles Buch. Lies es einmal durch und behalte es dann als Referenz, wenn du magst. –

+0

nette antwort !!! – Gob00st

20

Stan Lippman's BLog post schlägt vor: -

Stroustrup die bestehende Klasse Stichwort ein neues Schlüsselwort angeben wieder verwendet einen Typparameter statt einzuführen, natürlich könnten bestehende Programme brechen. Es war nicht so, dass ein neues Schlüsselwort nicht berücksichtigt wurde - nur dass es nicht als notwendig angesehen wurde angesichts seiner möglichen Störung . Und bis zum ISO-C++ - Standard, dies war der einzige Weg, einen Typparameter zu deklarieren.

Also im Grunde Stroustrup wiederverwendet Klasse Schlüsselwort, ohne ein neues Schlüsselwort einzuführen, die

danach im Standard aus den folgenden Gründen geändert wird

Wie das Beispiel gegeben

template <class T> 
class Demonstration { 
public: 
void method() { 
    T::A *aObj; // oops … 
    // … 
}; 

Sprache Grammatik falsch interpretiert T::A *aObj; als arithmetischer Ausdruck, so dass ein neues Schlüsselwort mit der Bezeichnung typename

typename T::A* a6; 
012 eingeführt wird

Es weist den Compiler an, die nachfolgende Anweisung als eine Deklaration zu behandeln.

Da das Stichwort auf der Gehaltsliste war, Heck, warum nicht die Verwirrung beheben verursacht durch die ursprüngliche Entscheidung die Klasse Schlüsselwort wiederzuverwenden.

Das ist, warum wir haben beide

Sie einen Blick auf this post haben kann, es wird definitiv helfen, ich von ihm so viel wie ich konnte es einfach extrahiert

+0

Ja, aber warum dann war ein neues Stichwort 'typename' erforderlich, wenn Sie das vorhandene Keyword' CLASS' für den gleichen Zweck verwenden könnte? – Jesper

+4

@Jesper: Ich denke, Xenus' Antwort hier ist verwirrend. 'Typename' wurde notwendig, das Parsen Problem zu beheben als mit einem Zitat Josuttis in Naveen Antwort beschrieben. (Ich glaube nicht, ein 'CLASS' an dieser Stelle einfügen würde gearbeitet haben.) Erst nach dem neuen Schlüsselwort für diesen Fall angenommen wurde, war es auch in Template-Argumente Erklärungen erlaubt (_or ist, dass Definitionen? _), Denn das' Klasse "war immer etwas irreführend. – sbi

11

Betrachten Sie den Code

template<class T> somefunction(T * arg) 
{ 
    T::sometype x; // broken 
    . 
    . 

Leider ist der Compiler nicht als psychische erforderlich ist, und nicht weiß, ob T :: sometype auf einen Typnamen oder ein statisches Element Bezug wird am Ende von T. So verwendet man typename es zu sagen:

template<class T> somefunction(T * arg) 
{ 
    typename T::sometype x; // works! 
    . 
    . 
4

zwei Verwendungen:

  1. Als Template-Argument Schlüsselwort (anstatt 'Klasse')
  2. A Typname Schlüsselwort weist den Compiler an, die eine Kennung einen Typ (anstelle einer statischen Elementvariable)
template <typename T> class X // [1] 
{ 
    typename T::Y _member; // [2] 
} 
3

Das ist Das Geheimnis liegt in der Tatsache, dass eine Vorlage für einige Arten spezialisiert werden kann. Dies bedeutet, dass es auch die Schnittstelle für verschiedene Typen völlig unterschiedlich definieren kann. Zum Beispiel können Sie schreiben:

Man könnte fragen, warum ist das nützlich und in der Tat: Das sieht wirklich nutzlos aus. Aber bedenken Sie, dass zum Beispiel std::vector<bool> der reference Typ ganz anders aussieht als für andere T s. Zugegeben, es ändert nicht die Art von reference von einem Typ zu etwas anderem, aber nichtsdestoweniger könnte es passieren.

Was passiert nun, wenn Sie Ihre eigenen Vorlagen mit dieser Vorlage test schreiben. So etwas wie diese

template<typename T> 
void print(T& x) { 
    test<T>::ptr p = &x; 
    std::cout << *p << std::endl; 
} 

scheint es ok für Sie, weil Sie erwarten dass test<T>::ptr ein Typ ist. Aber der Compiler weiß es nicht und in der Tat wird er vom Standard sogar dazu geraten, das Gegenteil zu erwarten, test<T>::ptr ist kein Typ. Um dem Compiler mitzuteilen, was Sie erwarten, müssen Sie vorher eine typename hinzufügen. Die richtige Vorlage sieht wie folgt aus

template<typename T> 
void print(T& x) { 
    typename test<T>::ptr p = &x; 
    std::cout << *p << std::endl; 
} 

Fazit: Sie haben typename hinzufügen, bevor, wenn Sie eine verschachtelte Art von Vorlage im Vorlagen verwenden. (Natürlich nur, wenn für diese innere Vorlage ein Vorlagenparameter Ihrer Vorlage verwendet wird.)

5

In einigen Situationen, in denen Sie von abhängige Typ beziehen (Bedeutung "abhängig von Vorlagenparameter"), die Der Compiler kann die semantische Bedeutung des resultierenden Konstrukts nicht immer eindeutig ableiten, weil er nicht weiß, um welche Art von Name es sich handelt (dh ob es sich um einen Namen eines Typs, einen Namen eines Datenmembers oder einen Namen von etwas anderem handelt). In solchen Fällen müssen Sie die Situation disambiguieren, 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 wird das Schlüsselwort typename in notwendig, dass der Code zu kompilieren.

Das gleiche passiert, wenn Sie zu einer Vorlage Mitglied der abhängigen Art beziehen möchten, das heißt zu einem Namen, der eine Vorlage bezeichnet. Sie müssen auch den Compiler helfen, indem das Schlüsselwort template, obwohl es anders In einigen Fällen

template <class T> struct S { 
    T::template ptr<int> p; 
}; 

platziert wird es notwendig sein, sowohl

template <class T> struct S { 
    typename T::template ptr<int>::type i; 
}; 

zu verwenden (wenn ich die Syntax richtig bekam) .

Natürlich eine andere Rolle des Schlüsselwort typename ist in der Schablonenparameterdeklarationen verwendet werden.

+0

Siehe auch [A Beschreibung des C++ Typname Schlüsselwort] (http://pages.cs.wisc.edu/~driscoll/typename.html) für mehr (Hintergrund-) Informationen. – Atafar

1
#include <iostream> 

class A { 
public: 
    typedef int my_t; 
}; 

template <class T> 
class B { 
public: 
    // T::my_t *ptr; // It will produce compilation error 
    typename T::my_t *ptr; // It will output 5 
}; 

int main() { 
    B<A> b; 
    int my_int = 5; 
    b.ptr = &my_int; 
    std::cout << *b.ptr; 
    std::cin.ignore(); 
    return 0; 
} 
Verwandte Themen