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"
A typedef declaration is a declaration with typedef as the storage class. The declarator becomes a new type. You can use typedef declarations to construct shorter or more meaningful names for types already defined by C or for types that you have declared.
Formally, if the typedef declaration defines an unnamed class or enum, the first typedef-name declared by the declaration to be that class type or enum type is used to denote the class type or enum type for linkage purposes only. For example, in typedef struct { /* ... */ } S;, S is a typedef-name for linkage purposes.
A typedef is scoped exactly as the object declaration would have been, so it can be file scoped or local to a block or (in C++) to a namespace or class.
However, the authoritative C/C++ reference website cplusplus says "typedef does not create different types.
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
.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With