name optional in a typedef declaration?

I was quite surprised when I saw the following code compile without errors or warnings in g++-4.2:

typedef enum test { one };

My assumption was that if you used the typedef keyword it would require an extra identifier as in:

typedef enum test { one } test;

As already mentioned, g++-4.2 accepts it without even a warning. Clang++ 3.0 warns "warning: typedef requires a name", similarly Comeau warns "warning: declaration requires a typedef name", and g++-4.6 informs: "warning: 'typedef' was ignored in this declaration".

I have not been able to identify where in the standard this is allowed, and I find it slightly confusing that two of the compilers warn that it is required, shouldn't it be an error if the typedef-name is required but not present?

UPDATE : I have checked in C with the same compilers. Clang and comeau yield the same output, gcc gives a warning: "warning: useless storage class specifier in empty declaration", which seems even more confusing.

UPDATE : I have checked removing the name of the enum and the results are the same:

typedef enum { one };

Similarly with a named struct:

typedef struct named { int x };

But not with an unnamed struct, in which case the code was rejected in g++ (4.2/4.6) with "error: missing type-name in typedef-declaration", gcc (4.2/4.6) gave a warning: "warning: unnamed struct/union that defines no instances", clang++ "warning: declaration does not declare anything", comeau "error: declaration requires a typedef name"


It is a degenerate syntax that is allowed but provides no benefit. Most modern compilers can be provoked into emitting a warning about it; by default, they may not. Without the typedef name, the keyword typedef is superfluous; in your example, it is completely equivalent to:

enum test { one };

Another place where it can occur is with a structure:

typedef struct SomeThing { int whatever; };

This is equivalent to:

struct SomeThing { int whatever; };

Note that typedef is officially (or syntactically) a 'storage class specifier', like static , extern , auto and register .


C Standard

In ISO/IEC 9899:1999 (that's the C standard), we find:

§6.7 Declarations

Syntax

declaration:

declaration-specifiers init-declarator-listopt;

declaration-specifiers:

storage-class-specifier declaration-specifiersopt

type-specifier declaration-specifiersopt

type-qualifier declaration-specifiersopt

function-specifier declaration-specifiersopt

init-declarator-list:

init-declarator

init-declarator-list , init-declarator

init-declarator:

declarator

declarator = initializer

And (as requested):

§6.7.1 Storage-class specifiers

Syntax

storage-class-specifier:

typedef

extern

static

auto

register

If you track through that syntax, there are a lot of degenerate possibilities, and what you showed is just one of the many.


C++ Standard

It is possible that C++ has different rules.

In ISO/IEC 14882:1998 (the original C++ standard), we find in §7.1.1 'Storage class specifiers' that C++ does not treat typedef as a storage class; the list adds mutable and excludes typedef . So, the grammatical specification of typedef in C++ is definitely different from the C specification.

§7 Declarations

Declarations specify how names are to be interpreted. Declarations have the form

declaration-seq:

declaration

declaration-seq declaration

declaration:

block-declaration

function-definition

template-declaration

explicit-instantiation

explicit-specialization

linkage-specification

namespace-definition

block-declaration:

simple-declaration

asm-definition

namespace-alias-definition

using-declaration

using-directive

simple-declaration:

decl-specifier-seqopt init-declarator-listopt ;

...

¶5 If the decl-specifier-seq contains the typedef specifier, the declaration is called a typedef declaration and the name of each init-declarator is declared to be a typedef-name, synonymous with its associated type (7.1.3).

§7.1 Specifiers [dcl.spec]

The specifiers that can be used in a declaration are

decl-specifier:

storage-class-specifier

type-specifier

function-specifier

friend

typedef

decl-specifier-seq:

decl-specifier-seqopt

decl-specifier

§7.1.1 Storage class specifiers [dcl.stc]

storage-class-specifier:

auto

register

static

extern

mutable

§7.1.2 Function specifiers [dcl.fct.spec]

function-specifier:

inline

virtual

explicit

§7.1.3 The typedef specifier [dcl.typedef]

Declarations containing the decl-specifier typedef declare identifiers that can be used later for naming fundamental (3.9.1) or compound (3.9.2) types. The typedef specifier shall not be used in a function-definition (8.4), and it shall not be combined in a decl-specifier-seq with any other kind of specifier except a type-specifier.

typedef-name:

identifier

...

In a given scope, a typedef specifier can be used to redefine the name of any type declared in that scope to refer to the type to which it already refers. [Example:

typedef struct s { /* ... */ } s;
typedef int I;
typedef int I;
typedef I I;

—end example]

§7.1.4 The friend specifier [dcl.friend]

The friend specifier is used to specify access to class members; see 11.4.

§7.1.5 Type specifiers [dcl.type]

type-specifier:

simple-type-specifier

class-specifier

enum-specifier

elaborated-type-specifier

cv-qualifier


Since §7 ¶5 says that typedef names come from the init-declarator and the init-declarator-list is tagged 'opt', I think that means that the typedef name can be omitted in C++, just as in C.


The only thing I could find was the following in the C++03 standard §7.1.3 [dcl.typedef] p1 :

typedef-name:

  • identifier
  • A name declared with the typedef specifier becomes a typedef-name.

    Notice the missing opt after identifier, which indicates, atleast to me, that an identifier is needed for the typedef-name. Strange that all tested compilers (silently) accept this.


    Edit : After @Jonathan's answer, I found the following in the same standard as above:

    decl-specifier:

  • storage-class-specifier
  • type-specifier
  • function-specifier
  • friend
  • typedef
  • As can be seen, it provides an extra case for typedef and the list on storage-class-specifiers confirms this:

    storage-class-specifier:

  • auto
  • register
  • static
  • extern
  • mutable
  • So, we're just as clueless as before in the C++ case.


    It really looks like a C vs. C++ difference, to me. C++ implicitly typedefs structs and unions to their tags; so adding the typedef is superfluous, but not an error. I don't know if this works for enums as well.

    The thing to do next is to see what variable definitions are permitted after these declarations.

    enum test etest;
    test etest2;
    struct named snamed;
    named snamed2;
    
    链接地址: http://www.djcxy.com/p/58710.html

    上一篇: 模板函数重载相同的签名,为什么这个工作?

    下一篇: 名称在typedef声明中是可选的?