What is the purpose of the Most Vexing Parse?
Let's say MVP didn't exist.
How would you declare a function?
A foo();
would be a variable definition, not a method declaration. Would you introduce a new keyword? Would you have a more awkward syntax for a function declaration? Or would you rather have
A foo;
define a variable and
A foo();
declare a function?
Your slightly more complicated example is just for consistency with this basic one. It's easier to say "everything that can be interpreted as a declaration, will be interpreted as a declaration" rather than "everything that can be interpreted as a declaration, will be interpreted as a declaration, unless it's a single variable definition, in which case it's a variable definition".
This probably isn't the motivation behind it though, but a reason it's a good thing.
Most vexing parse C++11
what exactly this line does
It creates a temporary X
, value-initialising it by calling the default constructor, and then uses that to initialise a Y
variable, calling the const X&
conversion constructor.
where is connection to Most vexing parse
If you were to try to write this using old-school initialisation syntax
Y y (X());
then the so-called "most vexing parse" would interpret this as a function, rather than a variable, declaration: a function called y
, with return type Y
and a single parameter, whose type is a (pointer to a) function returning X
.
You could add extra parentheses, so that it can't be interpreted as a function declaration:
Y y ((X()));
or, since C++11, you can use brace-initialisation as your example does.
Most vexing parse [duplicate]
what does this syntax
int(x)
in the statementFoo f( int(x) );
mean?
The parentheses around x
are superfluous and will be ignored. So int(x)
is the same as int x
here, which means a parameter named x
with type int
.
Is it the same as
Foo f( int x );
?
Yes. Foo f( int(x) );
, is a function declaration which is named f
, returns Foo
, takes one parameter named x
with type int
.
Here's the explanation from the standard. [dcl.ambig.res]/1:
(emphasis mine)
The ambiguity arising from the similarity between a function-style
cast and a declaration mentioned in [stmt.ambig] can also occur in the
context of a declaration. In that context, the choice is between a
function declaration with a redundant set of parentheses around a
parameter name and an object declaration with a function-style cast as
the initializer. Just as for the ambiguities mentioned in
[stmt.ambig], the resolution is to consider any construct that could
possibly be a declaration.Note: A declaration can be
explicitly disambiguated by adding parentheses around the argument.
The ambiguity can be avoided by use of copy-initialization or
list-initialization syntax, or by use of a non-function-style cast.struct S {
S(int);
};
void foo(double a) {
S w(int(a)); // function declaration
S x(int()); // function declaration
S y((int(a))); // object declaration
S y((int)a); // object declaration
S z = int(a); // object declaration
}
So, int(x)
will be considered as a declaration (of the parameter) rather than a function style cast.
Most vexing parse with array access
The reason is because in the context of a function declaration, the compiler will interpret std::string(argv[0])
as std::string argv[0]
, i.e. a declaration of a zero-sized array as the function parameter named argv
(overshadowing the argv
from main
, as this is a different scope), which then is equivalent to a pointer by array-to-pointer-decay.
Therefore, std::stringstream ss(std::string(argv[0]));
means the same as std::stringstream ss(std::string* argv);
Edit: As it got correctly annotaded in the comments, zero-sized array declarations are invalid in C++, rendering the program ill-formed. When compiling this code with -pedantic
flags (GCC and clang), warnings will be issued. Visual Studio even produces a compilation error. For any other array index than 0, the argumentation above however still holds.
How can I avoid most vexing parse with direct value initialization?
Use copy initialisation and rely on C++17's guarantee that copy elision will happen.
For example:
struct Foo
{
Foo() = default;
Foo(Foo const&) = delete;
};
int main()
{
auto f = Foo();
}
https://godbolt.org/g/9tbkjZ
Most vexing parse
This is due to the fact that TimeKeeper time_keeper(Timer());
is interpreted as a function declaration and not as a variable definition. This, by itself, is not an error, but when you try to access the get_time()
member of time_keeper (which is a function, not a TimeKeeper instance), your compiler fails.
This is how your compiler view the code:
int main() {
// time_keeper gets interpreted as a function declaration with a function argument.
// This is definitely *not* what we expect, but from the compiler POV it's okay.
TimeKeeper time_keeper(Timer (*unnamed_fn_arg)());
// Compiler complains: time_keeper is function, how on earth do you expect me to call
// one of its members? It doesn't have member functions!
return time_keeper.get_time();
}
Most vexing parse even more vexing
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
Related Topics
In What Cases Do I Use Malloc And/Or New
"Unpacking" a Tuple to Call a Matching Function Pointer
Erasing Elements from a Vector
How to Parse a String to an Int in C++
How to Read an Entire File into a Std::String in C++
Convert Char to Int in C and C++
Why Would We Call Cin.Clear() and Cin.Ignore() After Reading Input
Does "Undefined Behavior" Really Permit *Anything* to Happen
Variable Number of Arguments in C++
How to Stop C++ Console Application from Exiting Immediately
How to Convert Between Big-Endian and Little-Endian Values in C++
Difference Between G++ and Gcc
Why Do Function Pointer Definitions Work With Any Number of Ampersands '&' or Asterisks '*'
Using Getline(Cin, S) After Cin