10 Stimmen

Warum muss man manchmal `Typname T` statt nur `T` schreiben?

Ich habe den Wikipedia-Artikel über SFINAE und stieß auf folgendes Codebeispiel:

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
}

Nun habe ich tatsächlich schon einmal solchen Code geschrieben, und irgendwie wusste ich intuitiv, dass ich "typename T" statt nur "T" eingeben musste. Es wäre jedoch schön, die tatsächliche Logik dahinter zu kennen. Kann mir das jemand erklären?

14voto

jalf Punkte 235501

Die Kurzfassung, die Sie tun müssen typename X::Y immer dann, wenn X ein Schablonenparameter ist oder von ihm abhängt. Solange X nicht bekannt ist, kann der Compiler nicht sagen, ob Y ein Typ oder ein Wert ist. Sie müssen also Folgendes hinzufügen typename um anzugeben, dass es ein Typ ist.

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 anmerkt, liegt die Ursache für diese Unklarheit darin, dass Y kann ein statisches Mitglied, ein Enum oder eine Funktion sein. Ohne Kenntnis des Typs von X können wir nicht sagen. Der Standard legt fest, dass der Compiler davon ausgehen sollte, dass es sich um einen Wert handelt, es sei denn, er wird explizit als Typ gekennzeichnet, indem er die typename Stichwort.

Und es scheint, als wollten die Kommentatoren wirklich, dass ich auch einen anderen Fall erwähne, der damit zusammenhängt: ;)

Wenn der abhängige Name eine Funktionsschablone ist und Sie sie mit einem expliziten Schablonenargument aufrufen ( foo.bar<int>() zum Beispiel), müssen Sie die template Schlüsselwort vor dem Funktionsnamen, 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 Kleiner-als-Operator aufrufen möchten ( operator< ) darauf.

10voto

Alex Martelli Punkte 805329

Im Allgemeinen hat die (von C geerbte) Syntax von C++ einen technischen Defekt: Der Parser MUSS wissen, ob etwas einen Typ benennt oder nicht, sonst kann er bestimmte Mehrdeutigkeiten nicht auflösen (z. B. ist X * Y eine Multiplikation oder die Deklaration eines Zeigers Y auf Objekte des Typs X? es hängt alles davon ab, ob X einen Typ benennt...!-). Die typename Mit "Adjektiv" können Sie das bei Bedarf ganz klar und deutlich machen (was, wie eine andere Antwort erwähnt, typisch ist, wenn es um Vorlagenparameter geht;-).

3voto

CB Bailey Punkte 693084

Grundsätzlich benötigen Sie die typename Schlüsselwort, wenn Sie Schablonencode schreiben (d.h. Sie befinden sich in einer Funktions- oder Klassenschablone) und Sie sich auf einen Bezeichner beziehen, der von einem Schablonenparameter abhängt, von dem nicht bekannt ist, dass er ein Typ ist, der aber in Ihrem Schablonencode als Typ interpretiert werden muss.

In Ihrem Beispiel verwenden Sie typename T::Type bei Definition 1, weil T::Type hängt von dem Template-Parameter T und könnte ansonsten ein Datenmitglied sein.

Sie brauchen keine typename T bei Definition #2 als T wird als Teil der Schablonendefinition als Typ deklariert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X