3 ms·
The former syntax allows user defined types (that aren't reserved keywords) to be used, correct?
by webmaven 3y ago
The former syntax allows user defined types (that aren't reserved keywords) to be used, correct?
- trealira 3y agoWhat so you mean? You can declare user-defined types in C and C++ without that syntax. In C++: struct MyType { int field; }; MyType m; In C, you would need a typedef to get the same behavior, but the same applies otherwise. typedef struct { int field; } MyType;
- tialaramex 3y agoYou're correct. Of course the price for this is not only does it cause compiler authors to go bald earlier, it also means that you have natural language-style garden path parsing problems where the meaning of an expression isn't obvious at all and may take several attempts to discern. And so it also leads to situations where a human thinks "I made a Doodad" but the compiler sees instead "For some reason the human just decided to draw my attention to the fact Doodads exist, which I already knew". In a language like C or C++ where you do need to constantly repeat yourself because it's designed for 1970s compiler technology, warning about this will lead to too many false positives, so it's not done. C++ lock guards are a famous example where this happens - leading to code where authors and even sometimes reviewers believe a lock has been taken properly, but from the compiler's perspective there was never any lock at all so mutual exclusion isn't achieved.
- trealira 3y agoThis is mostly an issue with C++ specifically. If you implement the lexer hack[1], C can be parsed using an otherwise context-free grammar. C++ suffers from requiring arbitrary tokens of lookahead before being able to determine whether something is a declaration or an expression statement, which is a consequence of the combination of C-style declarations, function-like casts, and function-like constructors. For example, here, the parser cannot know whether the variables x, y, and z are being redeclared in the same scope, or if they're being evaluated and discarded, until it reaches the "new int" part. Last time I tested it, clang++ parsed it correctly, but not g++. int x, *y, *z; { int(x), *y, z, new int; } In C++, "int(x)" is equivalent to "(int) x" (and this doesn't exist in C), but as a holdover from C, it also allows redundant parentheses in declarations, so one could also just redeclare x in a new scope like this (in both C and C++): double x; { int(x); /* x is an int now */ x = 5; } I think this is a variant of the most vexing parse[2], which your lock guard example also sounds like an example of. You probably already know about this, but I was surprised by that example even knowing that C++ requires arbitrary tokens of lookahead to parse correctly, because I had only seen the Timekeeper example from Wikipedia. There's also the problem that it can't parse something like this unless it knows whether it's supposed to be a template instantiation or an expression statement, but I think that's just an extension of the typename/variable problem that it inherited from C. a < b, c > d; [1]: https://en.wikipedia.org/wiki/Lexer_hack https://en.wikipedia.org/wiki/Lexer_hack [2]: https://en.wikipedia.org/wiki/Most_vexing_parse https://en.wikipedia.org/wiki/Most_vexing_parse