What were they thinking?

Today I discovered one of the ugliest parts of the C++ standard — declarations.

We all know that C++ is an ugly language. Indeed, several times over, people have tried to reinvent it, ranging from new languages (MS’s C#) to entirely compatible, but novel grammars (see SPECS).

I’ve always suspected that such attempts are more an attempt to steal publicity from C++ rather than any more altruistic goal of fixing the language; indeed I think it’s almost impossible to fix something as weighed down by legacy as C++.

However, today I discovered some truly odd bits of the C++ standard in the declarations. You or I might consider:

typedef int i;

and

int i;

to be perfectly sensible things to write, and in some way I mentally separate the two — one is a type definition with no direct side-effect, the other is defining and instantiating an object. however, from the language’s point of view, these are both viewed as the same. Quite odd, you might think, but not too remarkable. Similarly,

int main();

is viewed as a ‘declaration’ too — yet no object is defined nor instantiated. Weirdness number 1 though is:

int a, main();

Firstly, I did a double-take when I realised this is legal. Seems a function declaration can be mixed in with other declarations (a definition cannot, however, though it looks rather similar. Secondly, this declaration statement doesn’t treat each term of the declaration (the standard calls these ‘declarators’) equally. The declarator a is a definition and instantiation, whereas main() is a declaration. To make matters more confusing, changing that line to be

extern int a, main();

makes both a and main() declarations. The extern turns the whole statement into a declaration, regardless of the type of the declarators. Ok — odd, but again not really too bad, just decidedly non-orthogonal.

The fun begins when one consults the C++ grammar again, and realises that from the point of view of the compiler the int, extern, and even typedef are treated as part and parcel of the same bit of the declaration, the type of objects being declared. Thus, we can write code like:

int typedef i;

to typedef ‘i’ as being an integer type. Add to this the fact that extened named types (short int, long double and the like) can be split up, and that class definitions are types too, and you can have all sorts of ‘fun’:

short typedef int ShortType;// defines ShortType to be short int

struct { int a; } typedef T;// defines T to be an unnamed structure containing an integer a

In conclusion, C++ is a lot more awkward than I first gave it credit for. Things like typedef and extern I took to be ‘things you prefix types with’, almost as if there was a whole special type of statement of typedef-declaration which always started with a typedef token. It would seem a lot of people think that too — while all the major vendors’ compilers accept the code, most academic and open source parsers I tested refused to accept this type of code. Top marks to EDG’s compiler, that at least warns that it’s a bit odd to put typedef halfway through your types.

CompilerParses funny classParses funny typedef
EDG's compiler (tested via Comeau online) yesyes
gcc 2.95yesyes
gcc 3.2yesyes
gcc 3.3yesyes
gcc 3.4yesyes
CL.EXE 13.00.9466yesyes
CL.EXE 13.10.3077yesyes
ProGrammar's example C++ parsernono
src2srcmlnono
OpenC++nono
Filed under: Coding
Posted at 17:18:00 GMT on 11th January 2005.

About Matt Godbolt

Matt Godbolt is a C++ developer living in Chicago. Follow him on Mastodon or Bluesky.