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.
Compiler | Parses funny class | Parses funny typedef |
---|---|---|
EDG's compiler (tested via Comeau online) | yes | yes |
gcc 2.95 | yes | yes |
gcc 3.2 | yes | yes |
gcc 3.3 | yes | yes |
gcc 3.4 | yes | yes |
CL.EXE 13.00.9466 | yes | yes |
CL.EXE 13.10.3077 | yes | yes |
ProGrammar's example C++ parser | no | no |
src2srcml | no | no |
OpenC++ | no | no |
Matt Godbolt is a C++ developer living in Chicago. Follow him on Mastodon or Bluesky.