28

In the following code

#include <map>
#include <string>

struct P2d {
    double x, y;
    P2d(double x, double y) : x(x), y(y) {}
};

double bar() {
    std::map<std::string, int> m;
    //P2d lp = P2d(double(m["x"]), double(m["y"])); // this works
    P2d lp(double(m["x"]), double(m["y"]));
    return lp.x;
}

all compilers I tested agree the code (un-commented version) is invalid but I fail to see why the definition

 P2d lp(<double>, <double>);

that I used is not acceptable.

I remember the rule was "if it can be both a function declaration and a definition then it's a declaration" but I expected that if it cannot be a declaration then it should be interpreted as a definition instead of giving an error.

What am I missing?

6502
  • 112,025
  • 15
  • 165
  • 265
  • According to [this](https://en.wikipedia.org/wiki/Most_vexing_parse#Example_with_functions), I would expect `P2d lp((double)m["x"], (double)m["y"]);` to work. – 0x5453 Jun 22 '20 at 15:45
  • @0x5453: there are many ways to get it working (braces, or the code commented, or using `static_cast`). My question is why this code is NOT working... – 6502 Jun 22 '20 at 15:46
  • 1
    Interesting. I found this searching for the note: https://stackoverflow.com/questions/21624880/how-does-this-declaration-invoke-the-most-vexing-parse. Seems like that answer is valid? – Mikael H Jun 22 '20 at 15:51
  • @MikaelH: that source is ambiguous and the rule says that in such a case the function declaration prevails. What I don't understand if why the code in the question (that is NOT ambiguous because it cannot parse as a function declaration) isn't cosidered a valid definition of a P2d instance. – 6502 Jun 22 '20 at 15:53
  • 1
    I can only guess, but I would have thought the same reason as for that post. `P2d lp(double(m["x"]), double(m["y"]));` is parsed as `P2d lp(double m["x"], double m["y"]);` => `P2d lp(double* m, double* m);` – Mikael H Jun 22 '20 at 16:01

1 Answers1

30

Hold on to your chair since it's pretty funny. As you surely know C++ allows array function parameters. And so you can get this:

void foo(double s[2], double b[2]);

This is obvious. A possible obfuscation step is to replace spaces between type and parameters name which is also allowed:

void foo(double(s[2]),double(b[2]));

Now you can imagine what can be done pretty simply - replace numbers with const char*. Like this:

void foo(double(s["x"]),double(b["y"]));

This is invalid function declaration, nevertheless it is seen by the compilers as exactly this - declaration. This is exactly what happened to your code.

EDIT: The whole problem seems to arise from not strict enough restrictions on array declarators in C++ standard. The only requirement for array 'size' parameter is being constexpr value which is supposed to be converted to std::size_t (but it is not checked on the level of syntax analysis, it is done later on). For more on that check this

bartop
  • 9,971
  • 1
  • 23
  • 54
  • OP argues that he wants to know why the compiler couldn't then walk back on its parse as a function declaration and parse it as a variable definition (in a second step, I guess). What a compiler writer would need to answer is all the hell that would break loose if we were to require compilers to backtrack and try alternative parses. – Jeffrey Jun 22 '20 at 16:03
  • Array parameters were my first thought also, but that is not a valid array parameter because there is `"x"` and not `3` between the brackets. So it's not a valid function declaration, why is it parsed as a function declaration anyway (just to give error later)? For example in `int x(3);` the part inside the parenthesis is invalid as parameters, but in this case the compiler simply accepts it as a definition of an integer. Where is the line? where is it stated formally? – 6502 Jun 22 '20 at 16:09
  • @6502 just found it in standard. It's all about messy array declarators. I suggest you read this: https://timsong-cpp.github.io/cppwp/dcl.decl#dcl.array – bartop Jun 22 '20 at 16:14
  • Indeed this seems the issue. Somehow I thought that the rules were similar to the ones used for SFINAE where a common trick is to use a negative sized array but it seems here the declaration is ruled out at a different level. If it passes the syntax check (where as you noted any constant expression is accepted) then it's a declaration and no backtracking is possible after that. In other words C++ syntax is just a nightmare :-D ... – 6502 Jun 22 '20 at 16:27
  • @6502 Yup, C++ is nightmare. Very beautiful, yet a nightmare. We should be thankful to the compilers' implementors ;) – bartop Jun 22 '20 at 16:30
  • 6
    Sometimes I wonder if the world would be better if implementers refused to follow committee decision down nonsense paths (like allowing code-block level declarations of free functions; something for which I can think to no sensible use and from which all the messy most vexing parse started). – 6502 Jun 22 '20 at 17:10
  • 1
    Given this answer, question's title is absolutely spot-on. – davidbak Jun 23 '20 at 02:33
  • @Jeffrey - for automatically doing alternative parses: that's what [GLR parsing](https://en.wikipedia.org/wiki/GLR_parser) is for. – davidbak Jun 23 '20 at 02:36