Unterschied der Schlüsselwörter 'Typname' und 'Klasse' in Vorlagen?

504

Für Vorlagen habe ich beide Erklärungen gesehen:

template < typename T >
template < class T >

Was ist der Unterschied?

Und was genau bedeuten diese Schlüsselwörter im folgenden Beispiel (entnommen aus dem deutschen Wikipedia-Artikel über Vorlagen)?

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};
Matte
quelle

Antworten:

430

typenameund classsind im Grundfall der Angabe einer Vorlage austauschbar:

template<class T>
class Foo
{
};

und

template<typename T>
class Foo
{
};

sind gleichwertig.

Allerdings gibt es spezielle Fälle, in denen es einen Unterschied zwischen typenameund gibt class.

Der erste ist bei abhängigen Typen. typenamewird verwendet, um zu deklarieren, wenn Sie auf einen verschachtelten Typ verweisen, der von einem anderen Vorlagenparameter abhängt, wie typedefin diesem Beispiel:

template<typename param_t>
class Foo
{
    typedef typename param_t::baz sub_t;
};

Der zweite, den Sie tatsächlich in Ihrer Frage zeigen, obwohl Sie ihn möglicherweise nicht erkennen:

template < template < typename, typename > class Container, typename Type >

Bei der Angabe einer Vorlagenvorlageclass MUSS das Schlüsselwort wie oben verwendet werden - es ist in diesem Fall nicht austauschbar (Hinweis: In C ++ 17 sind in diesem Fall beide Schlüsselwörter zulässig) .typename

Sie müssen auch Folgendes verwenden, classwenn Sie eine Vorlage explizit instanziieren:

template class Foo<int>;

Ich bin mir sicher, dass es andere Fälle gibt, die ich verpasst habe, aber das Fazit lautet: Diese beiden Schlüsselwörter sind nicht gleichwertig, und dies sind einige häufige Fälle, in denen Sie das eine oder das andere verwenden müssen.

Aaron Klotz
quelle
45
Letzteres ist so ziemlich ein Sonderfall der Tatsache, dass Sie Klasse oder Struktur und nicht Typname verwenden müssen, um eine Klasse zu definieren. Offensichtlich konnte keines Ihrer ersten beiden Codebits durch ersetzt werden template <typename T> typename Foo {};, da Foo <T> definitiv eine Klasse ist.
Steve Jessop
2
std::vector<int>::value_typeist kein abhängiger Typ, Sie brauchen ihn typenamedort nicht - Sie brauchen ihn nur, wenn ein Typ von einem Vorlagenparameter abhängt, sagen wirtemplate<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche
2
Und wieder param_tist kein abhängiger Typ. Abhängige Typen sind Namen, die von einem Vorlagenparameter abhängig sind , z. B. foo<param_t>::some_typekeine Vorlagenparameter selbst.
Georg Fritzsche
2
Ein C ++ 1z Vorschlag N4051 ermöglicht es Ihnen , zu verwenden typename, das heißt template <typename> typename C.
user4112979
4
Ab GCC 5, G ++ ermöglicht nun Typname in einem Template - Template - Parameter .
Chnossos
95

Zum Benennen von Vorlagenparametern typenameund classsind äquivalent. §14.1.2:

Es gibt keinen semantischen Unterschied zwischen Klasse und Typname in einem Vorlagenparameter.

typenameIn einem anderen Kontext ist es jedoch möglich, Vorlagen zu verwenden, um den Compiler darauf hinzuweisen, dass Sie auf einen abhängigen Typ verweisen. §14.6.2:

Es wird angenommen, dass ein Name, der in einer Vorlagendeklaration oder -definition verwendet wird und von einem Vorlagenparameter abhängig ist, keinen Typ benennt, es sei denn, die entsprechende Namenssuche findet einen Typnamen oder der Name wird durch das Schlüsselwort Typname qualifiziert.

Beispiel:

typename some_template<T>::some_type

Ohne typenameden Compiler kann man im Allgemeinen nicht sagen, ob man sich auf einen Typ bezieht oder nicht.

Georg Fritzsche
quelle
2
Ich verstehe die Regel, aber was genau hindert den Compiler daran, some_template <T> intern als Typ zu behandeln? Entschuldigung, wenn mir etwas Offensichtliches fehlt.
Batbrat
23

Obwohl es keinen technischen Unterschied gibt, habe ich gesehen, dass die beiden verwendet wurden, um leicht unterschiedliche Dinge zu bezeichnen.

Für eine Vorlage, die einen beliebigen Typ als T akzeptieren sollte, einschließlich integrierter Elemente (z. B. eines Arrays).

template<typename T>
class Foo { ... }

Für eine Vorlage, die nur funktioniert, wenn T eine echte Klasse ist.

template<class T>
class Foo { ... }

Aber denken Sie daran, dass dies nur ein Stil ist, den manche Leute benutzen. Nicht vom Standard vorgeschrieben oder von Compilern durchgesetzt

Michael Anderson
quelle
15
Ich beschuldige Sie nicht, es erwähnt zu haben, aber ich denke, diese Richtlinie ist ziemlich falsch, da Programmierer sich am Ende Zeit nehmen, über etwas nachzudenken, das keine Rolle spielt ("Habe ich das richtige verwendet?"), Um auf etwas hinzuweisen, das nicht wichtig ist. t matter ("Gibt es einen integrierten Typ, der die für diesen Vorlagenparameter erforderliche Schnittstelle implementiert?"). Wenn Mitglieder des Template-Parameters verwendet werden ( T t; int i = t.toInt();), benötigen Sie eine "echte Klasse", und Ihr Code wird nicht kompiliert, wenn Sie intfür T...
Steve Jessop
1
Wenn Sie die Verwendung auf tatsächliche Klassen beschränken möchten, sollten Sie eine Spezialisierung hinzufügen, um einen Fehler für Nicht-Klassentypen auszulösen. Wenn Sie die Verwendung auf bestimmte Klassen beschränken möchten, spezialisieren Sie sich nur auf diese. In jedem Fall ist eine solche stilistische Unterscheidung zu subtil, um die Botschaft zu vermitteln.
Potatoswatter
2
Da sie dasselbe bedeuteten, verwenden Sie bitte nur eine. Ansonsten ist es so, als würden Sie Inline {verwenden, es sei denn, es ist ein Dienstag, und dann verwenden Sie Next-Line {.
Paul Draper
+1 Ich mache das manchmal selbst ... classimpliziert, dass Sie nicht nur einen "Wert" erwarten, der möglicherweise einige Operatoren unterstützt, Konstruktionen und / oder Zuweisungen kopiert oder verschiebt, sondern speziell einen Typ benötigen, der eine Semantik des Mitgliederzugriffs unterstützt. Der schnellste Blick auf die Deklaration setzt dann Erwartungen und entmutigt, z. B. eingebaute Typen für classParameter bereitzustellen, wenn dies sicherlich ein Fehler wäre.
Tony Delroy
Ich möchte verstehen, welche Arten von realen Situationen existieren, in denen eine Vorlage für JEDE Klasse funktionieren würde, aber nicht mit integrierten Typen. Hast du ein beispiel
lfalin
7
  1. Kein Unterschied
  2. Der Vorlagentypparameter Containerist selbst eine Vorlage mit zwei Typparametern.
Nikolai Fetissov
quelle
3
Es gibt einen Unterschied im Allgemeinen.
Hassan Syed
Könnten diese beiden Parameter, mit denen der Container versehen ist, auch benannt werden? Im Beispiel haben sie keine Namen. Und auch - in diesem Beispiel heißt es "Klasse Container" - könnte stattdessen auch "Typname Container" geschrieben werden?
Mat
2
@Mat: Ja, der Begriff, nach dem gesucht werden soll, sind Parameter / Argumente für Vorlagenvorlagen . ZB:template<template<class U> class V> struct C {};
Georg Fritzsche
6

Dieses Snippet stammt aus dem C ++ - Primer-Buch. Obwohl ich sicher bin, dass das falsch ist.

Vor jedem Typparameter muss die Schlüsselwortklasse oder der Typname stehen:

// error: must precede U with either typename or class
template <typename T, U> T calc(const T&, const U&);

Diese Schlüsselwörter haben dieselbe Bedeutung und können innerhalb einer Vorlagenparameterliste austauschbar verwendet werden. Eine Vorlagenparameterliste kann beide Schlüsselwörter verwenden:

// ok: no distinction between typename and class in a template parameter list
template <typename T, class U> calc (const T&, const U&);

Es mag intuitiver erscheinen, das Schlüsselwort Typname anstelle der Klasse zu verwenden, um einen Vorlagentypparameter zu bestimmen. Schließlich können wir integrierte (Nichtklassen-) Typen als Vorlagentypargument verwenden. Darüber hinaus zeigt der Typname deutlicher an, dass der folgende Name ein Typname ist. Der Typname wurde jedoch zu C ++ hinzugefügt, nachdem Vorlagen bereits weit verbreitet waren. Einige Programmierer verwenden weiterhin ausschließlich Klassen

KK
quelle