Ich war ziemlich überrascht, als ich den folgenden Code ohne Fehler oder Warnungen in g ++ - 4.2 kompilieren sah:
typedef enum test { one };
Meine Annahme war, dass bei Verwendung des Schlüsselworts typedef
ein zusätzlicher Bezeichner erforderlich ist, wie in:
typedef enum test { one } test;
Wie bereits erwähnt, akzeptiert g ++ - 4.2 es ohne Warnung. Clang ++ 3.0 warnt " Warnung: typedef erfordert einen Namen ", ähnlich warnt Comeau " Warnung: Deklaration erfordert einen typedef-Namen ", und g ++ - 4.6 informiert: " Warnung: ' typedef 'wurde in dieser Deklaration ignoriert ".
Ich konnte nicht identifizieren, wo dies im Standard zulässig ist, und ich finde es etwas verwirrend, dass zwei der Compiler davor warnen, dass es sich um Erforderlich handelt, sollte es sich nicht um einen Fehler handeln, wenn der typedef-Name verwendet wird ist erforderlich aber nicht vorhanden?
UPDATE: Ich habe C mit den gleichen Compilern eingecheckt. Clang und Comeau liefern die gleiche Ausgabe, gcc gibt eine Warnung aus: " Warnung: nutzloser Speicherklassen-Bezeichner in leerer Deklaration ", was noch verwirrender erscheint.
UPDATE: Ich habe das Entfernen des Namens der Aufzählung überprüft und die Ergebnisse sind die gleichen:
typedef enum { one };
Ähnlich mit einer benannten Struktur:
typedef struct named { int x };
Aber nicht mit einer unbenannten Struktur. In diesem Fall wurde der Code in g ++ (4.2/4.6) mit " Fehler: fehlender Typname in typedef-Deklaration " zurückgewiesen. Gcc (4.2/4.6) gab eine Warnung aus : " Warnung: unbenannte Struktur/Union, die keine Instanzen definiert ", clang ++ " Warnung: Deklaration deklariert nichts ", comeau " Fehler: Deklaration erfordert einen typedef-Namen "
Es ist eine entartete Syntax, die erlaubt ist, aber keinen Nutzen bringt. Die meisten modernen Compiler können dazu gebracht werden, eine Warnung darüber auszusenden. Standardmäßig ist dies möglicherweise nicht der Fall. Ohne den Typedef-Namen ist das Schlüsselwort typedef
überflüssig. In Ihrem Beispiel ist es völlig gleichbedeutend mit:
enum test { one };
Ein anderer Ort, an dem es vorkommen kann, ist eine Struktur:
typedef struct SomeThing { int whatever; };
Das ist äquivalent zu:
struct SomeThing { int whatever; };
Beachten Sie, dass typedef
offiziell (oder syntaktisch) ein 'Speicherklassenspezifizierer' ist, wie static
, extern
, auto
und register
.
In ISO/IEC 9899: 1999 (das ist der C-Standard) finden wir:
§6.7 Erklärungen
Syntax
Deklaration:
Deklarationsangaben init-Deklarator-Listeopt;
Deklarationsangaben:
Speicherklassenspezifizierer deklarationsspezifiziereropt
Typbezeichner deklarationsspezifiziereropt
Typqualifizierer deklarationsspezifiziereropt
Funktionsbezeichner deklarationsspezifiziereropt
init-deklarator-list:
init-deklarator
Init-Deklaratorliste, Init-Deklarator
init-deklarator:
Deklarator
Deklarator = Initialisierer
Und (wie gewünscht):
§6.7.1 Speicherklassenspezifizierer
Syntax
speicherklassenspezifizierer:
typedef
extern
static
auto
register
Wenn Sie diese Syntax nachverfolgen, gibt es viele entartete Möglichkeiten. Was Sie gezeigt haben, ist nur eine von vielen.
Es ist möglich, dass C++ andere Regeln hat.
In ISO/IEC 14882: 1998 (dem ursprünglichen C++ - Standard) finden wir in §7.1.1 "Speicherklassenspezifizierer", dass C++ typedef
nicht als Speicherklasse behandelt. Die Liste fügt mutable
hinzu und schließt typedef
aus. Die grammatikalische Spezifikation von typedef
in C++ unterscheidet sich also definitiv von der C-Spezifikation.
§7 Erklärungen
Deklarationen legen fest, wie Namen interpretiert werden sollen. Erklärungen haben die Form
erklärung-seq:
-Deklaration
Deklaration der Deklaration
erklärung:
Blockdeklaration
Funktionsdefinition
Template-Deklaration
explizite Instantiierung
explizite Spezialisierung
Linkage-Spezifikation
Namespace-Definition
blockdeklaration:
einfache Deklaration
asm-definition
Namespace-Alias-Definition
using-Deklaration
using-Direktive
einfache Deklaration:
decl-specifier-seqopt Init-Deklarator-Listeopt ;
...
¶5 Wenn der decl-specifier-seq enthält den
typedef
-Bezeichner, die Deklaration wird alstypedef
-Deklaration und .__ bezeichnet. der Name jedesinit-declarator
ist ein typedef-name auch mit dem zugehörigen Typ (7.1.3).§7.1 Bezeichner [dcl.spec]
Die Spezifizierer, die in einer Deklaration verwendet werden können, sind
deklarationsspezifizierer:
Speicherklassenspezifizierer
Typbezeichner
Funktionsbezeichner
friend
typedef
decl-specifier-seq:
decl-specifier-seqopt
Deklarationsspezifizierer
§7.1.1 Speicherklassenspezifizierer [dcl.stc]
speicherklassenspezifizierer:
auto
register
static
extern
mutable
§7.1.2 Funktionsbezeichner [dcl.fct.spec]
funktionsspezifizierer:
inline
virtual
explicit
§7.1.3 Der Typedef-Bezeichner [dcl.typedef]
Deklarationen, die den Deklarationsbezeichner enthalten
typedef
deklariert Bezeichner, die später zur Benennung von .__ verwendet werden können. grundlegende (3.9.1) oder zusammengesetzte (3.9.2) Typen. Dertypedef
-Bezeichner darf nicht in einer Funktionsdefinition verwendet werden (8.4), und es darf nicht in einem Deklarationsbezeichner-seq .__ zusammengefasst werden. mit anderen Bezeichnern außer einen Typbezeichner.typedef-name:
Kennung
...
In einem bestimmten Bereich kann ein Typedef-Bezeichner verwendet werden, um den Namen eines beliebigen in diesem Bereich deklarierten Typs neu zu definieren um auf den Typ zu verweisen, auf den es sich bereits bezieht. [Beispiel:
typedef struct s { /* ... */ } s; typedef int I; typedef int I; typedef I I;
- Ende Beispiel]
§7.1.4 Der Friend-Bezeichner [dcl.friend]
Mit dem Friend-Bezeichner wird der Zugriff auf Klassenmitglieder angegeben. siehe 11.4.
§7.1.5 Typbezeichner [dcl.type]
typbezeichner:
einfacher Typbezeichner
Klassenspezifizierer
Aufzählungszeichen
spezifizierter Typbezeichner
cv-qualifier
Da §7 ¶5 sagt, dass typedef
Namen vom init-deklarator stammen und die init-declarator-list 'opt' markiert ist, glaube ich dass der typedef
-Name genau wie in C in C++ weggelassen werden kann.
Das einzige, was ich finden konnte, war Folgendes im C++ 03-Standard §7.1.3 [dcl.typedef] p1
:
typedef-name:
- bezeichner
Ein mit dem
typedef
-Bezeichner deklarierter Name wird zu einem typedef-name.
Beachten Sie das fehlende opt nach bezeichner, was zumindest mir anzeigt, dass ein bezeichner für den typedef-name benötigt wird. Seltsam, dass alle getesteten Compiler dies (leise) akzeptieren.
Edit : Nach @ Jonathans Antwort fand ich folgendes im selben Standard wie oben:
decl-specifier:
- Speicherklassenspezifizierer
- Typbezeichner
- Funktionsbezeichner
friend
typedef
Wie zu sehen ist, wird ein zusätzlicher Fall für typedef
bereitgestellt, und die Liste unter storage-class-speciers bestätigt dies:
speicherklassenspezifizierer:
auto
register
static
extern
mutable
Wir sind also im C++ - Fall genauso ahnungslos wie zuvor.
Es sieht für mich wirklich wie ein Unterschied zwischen C und C++ aus. C++ impliziert implizit Strukturen und Vereinigungen für ihre Tags. Das Hinzufügen des Typedefs ist daher überflüssig, jedoch kein Fehler. Ich weiß nicht, ob dies auch für Enummen funktioniert.
Als Nächstes müssen Sie sehen, welche Variablendefinitionen nach diesen Deklarationen zulässig sind.
enum test etest;
test etest2;
struct named snamed;
named snamed2;