2009-08-08 5 views
10

ich den Wikipedia-Artikel über SFINAE und traf folgendes Codebeispiel zu lesen:Warum müssen Sie manchmal `typename T` anstelle von` T` schreiben?

struct Test 
{ 
    typedef int Type; 
}; 

template < typename T > 
void f(typename T::Type) {} // definition #1 

template < typename T > 
void f(T) {}    // definition #2 

void foo() 
{ 
    f<Test> (10); //call #1 

    f<int> (10); //call #2 without error thanks to SFINAE 
} 

Jetzt habe ich tatsächlich geschriebenen Code, wie dies vor, und irgendwie intuitiv wusste ich, dass ich brauchte „typename T“ statt tippen nur "T". Es wäre jedoch nett, die eigentliche Logik dahinter zu kennen. Wer möchte es erklären?

+1

Ich empfehle Ihnen, die Vorlage faq zu lesen: http://womble.decadentplace.org.uk/c++/template-faq.html –

Antwort

8

Im Allgemeinen hat die C++ - Syntax (geerbt von C) einen technischen Defekt: Der Parser MUSS wissen, ob etwas einen Typ benennt oder nicht, sonst kann er bestimmte Mehrdeutigkeiten nicht lösen (zB ist X * Y eine Multiplikation oder die Deklaration eines Zeigers Y auf Objekte vom Typ X? Alles hängt davon ab, ob X einen Typ ... benennt! Das "Adjektiv" typename lässt Sie das bei Bedarf klar und deutlich machen (was, wie eine andere Antwort erwähnt, typisch ist, wenn Template-Parameter beteiligt sind ;-).

+1

Meinst du "in der Regel, wenn Template-Parameter beteiligt sind" (in diesem Fall bin ich mir ziemlich sicher, dass es * nur * wenn Template-Parameter beteiligt sind), oder ist das ein Tippfehler für "was typisch ist, wenn Template-Parameter beteiligt sind" ? –

+1

@onebyone, guter Fang, ich meinte tatsächlich "typisch" und nicht "typisch", redigierte meine Antwort zu reparieren, tx. –

13

Die kurze Version, die Sie tun müssen, typename X::Y immer wenn X ist oder hängt von einem Vorlagenparameter ab. Bis X bekannt ist, kann der Compiler nicht feststellen, ob Y ein Typ oder ein Wert ist. Sie müssen typename hinzufügen, um anzugeben, dass es sich um einen Typ handelt.

Zum Beispiel:

template <typename T> 
struct Foo { 
    typename T::some_type x; // T is a template parameter. `some_type` may or may not exist depending on what type T is. 
}; 

template <typename T> 
struct Foo { 
    typename some_template<T>::some_type x; // `some_template` may or may not have a `some_type` member, depending on which specialization is used when it is instantiated for type `T` 
}; 

Wie sbi in den Kommentaren weist darauf hin, die Ursache der Mehrdeutigkeit ist, dass Y könnte ein statisches Element, eine Enumeration oder eine Funktion sein. Ohne den Typ von X zu kennen, können wir nicht sagen. Der Standard gibt an, dass der Compiler davon ausgehen sollte, dass es sich um einen Wert handelt, es sei denn, er ist explizit mit dem Schlüsselwort typename als Typ gekennzeichnet.

Und es klingt wie die commen will, dass mir wirklich auch anderen verwandten Fall erwähnen:;)

Wenn der abhängige Name ein Funktionselement Vorlage ist, und rufen Sie es mit einem expliziten Template-Argumente (foo.bar<int>(), für Beispiel), müssen Sie das Schlüsselwort template vor dem Funktionsnamen hinzufügen, wie in foo.template bar<int>().

Der Grund dafür ist, dass der Compiler ohne das Schlüsselwort template davon ausgeht, dass bar ein Wert ist, und Sie den weniger als Operator (operator<) darauf aufrufen möchten.

+0

jalf, könnte es sich lohnen, eine alternative Bedeutung von 'T :: etwas zu erwähnen (statische Daten, Funktionsname). Und wenn Sie einmal so gut darin sind, könnte es sich lohnen, die Erklärung hinzuzufügen, warum wir manchmal auch eine 'Schablone' injizieren müssen. Es hat den gleichen Grund, also wird es nicht viel länger dauern. – sbi

+0

'Vorlage' ist jedoch nicht die gleiche Argumentation. Es hat nichts mit abhängigen Namen zu tun. Es fällt in die Kategorie "Probleme, die dazu neigen, Sie mit Vorlagen zu stolpern", aber ich glaube nicht, dass es der gleiche Grund ist wie bei "Typname". – jalf

+1

@jalf: Hat das nicht mit Vorlagennamen zu tun? In jedem Fall sind die Gründe ziemlich analog. Es ist nur im Vorlagencode erlaubt und es muss verwendet werden, um abhängige Mitgliedsnamen, die Namen von Vorlagen sind, von abhängigen Mitgliedsnamen zu unterscheiden, die dies nicht sind. –

3

Grundsätzlich benötigen Sie das Schlüsselwort typename, wenn Sie Vorlagencode schreiben (dh Sie befinden sich in einer Funktionsvorlage oder Klassenvorlage) und Sie beziehen sich auf einen Identifikator, der von einem Vorlagenparameter abhängt, der möglicherweise nicht bekannt ist Typ, muss aber als Typ in Ihrem Vorlagencode interpretiert werden.

In Ihrem Beispiel verwenden Sie typename T::Type bei Definition # 1, da T::Type auf den T Template-Parametern abhängig und könnte sonst ein Datenelement sein.

Sie brauchen nicht typename T bei Definition # 2, da T als Teil der Vorlagendefinition deklariert wird.

Verwandte Themen