Is the typedef-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 nam开发者_开发问答ed 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 atypedef
declaration and the name of eachinit-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. Thetypedef
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;
精彩评论