web-dev-qa-db-de.com

Ist der Typedef-Name in einer Typedef-Deklaration optional?

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.


C Standard

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.


C++ - Standard

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 als typedef-Deklaration und .__ bezeichnet. der Name jedes init-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. Der typedef-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.

41

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.

3
Xeo

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;
0
luser droog