Is the typedef-name optional in a typedef declaration?

36,375

Solution 1

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.

Solution 2

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.

Share:
36,375
David Rodríguez - dribeas
Author by

David Rodríguez - dribeas

David Rodríguez Ibeas Telecom engineer New York City, NY, USA [Un]defined behavior (blog) LinkedIn

Updated on June 20, 2020

Comments

  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 4 years

    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"

  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 13 years
    This is as far as I got, there is no optionality there, but I was not able to find where in the grammar typedef-name is used, in case that the optionality was marked there (something like typedef type-specifier typedef-name_opt
  • Xeo
    Xeo almost 13 years
    @David: Yeah, I can't find something like that too. :/ Maybe the C89 standard is more elaborate on this one.
  • Xeo
    Xeo almost 13 years
    So it was in the C standard. Let me find that in the C++ one. Edit: Actually, in C++, the typedef is handled as an extra case. See §7.1(.1).
  • Steve Jessop
    Steve Jessop almost 13 years
    Eh? Clang, Comeau and g++4.6 all warned. That means they didn't "silently accept" it. If it's an error then they diagnosed the error, the C++ standard only requires that conforming implementations diagnose ill-formed programs. It doesn't require that conforming implementations refuse to compile them, although neither does it define their behavior. It's perfectly legal for a conforming C++ compiler to use the C meaning of that syntax production, provided it warns.
  • Xeo
    Xeo almost 13 years
    @Steve: I put the "silently" in parens, to indicate that I meant only some of them.
  • Steve Jessop
    Steve Jessop almost 13 years
    ah, OK. Well then my tentative explanation is that g++ 4.2 was a bug, and for the others because they're all C compilers too under the covers, and because the C meaning is harmless, their authors considered it reasonable to use the C meaning. If you use -Werror then you don't have to fret about the difference between diagnosing and rejecting :-)
  • Jens Gustedt
    Jens Gustedt almost 13 years
    @Jonathan, does that mean that this syntax is valid in C and invalid in C++? At least from the name decl-specifier I'd deduce that this must be in the declaration of an identifier, and since there is none this would be invalid? (That some C++ compilers accept it is another thing.)
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 13 years
    This answer is unrelated to the question, which is specifically whether the typedef-name in the typedef is optional or not. Also, the use of typedef in C++ is not superfluous, as it affects the interpretation of the code, you can read more in this answer to an older question.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas almost 13 years
    This does provide a solution to some of the questions that I had about the typedef so I am accepting it. But it does not explain why typedef struct {}; is diagnosed in g++ as an error while typedef enum {}; isn't (on the empty unnamed struct, gcc just warns, clang/clang++ warn, comeau rejects the code both in C and C++ mode...)
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 13 years
    @David that appears to be a bug in GCC. it's only an error in C++ if the decl-specifier-seq does not introduce any names into the translation unit. It seems that GCC assumes that any enumeration has enumerators and will declare those as names. But for an empty enumeration, that assumption doesn't hold, of course.