3 ms·
What 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
by trealira 3y ago
What 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