Most vexing parseThe most vexing parse is a counterintuitive form of syntactic ambiguity resolution in the C++ programming language. In certain situations, the C++ grammar cannot distinguish between the creation of an object parameter and specification of a function's type. In those situations, the compiler is required to interpret the line as a function type specification. OccurrenceThe term "most vexing parse" was first used by Scott Meyers in his 2001 book Effective STL.[1] While unusual in C, the phenomenon was quite common in C++ until the introduction of uniform initialization in C++11.[2] ExamplesC-style castsA simple example appears when a functional cast is intended to convert an expression for initializing a variable: void f(double my_dbl) {
int i(int(my_dbl));
}
Line 2 above is ambiguous. One possible interpretation is to declare a variable // A function named i takes an integer and returns an integer.
int i(int my_dbl);
Unnamed temporaryA more elaborate example is: struct Timer {};
struct TimeKeeper {
explicit TimeKeeper(Timer t);
int get_time();
};
int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
The line TimeKeeper time_keeper(Timer());
is ambiguous, since it could be interpreted either as
The C++ standard requires the second interpretation, which is inconsistent with the subsequent line 10 above. For example, Clang++ warns that the most vexing parse has been applied on line 9 and errors on the subsequent line 10:[3] $ clang++ time_keeper.cc timekeeper.cc:9:25: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse] TimeKeeper time_keeper(Timer()); ^~~~~~~~~ timekeeper.cc:9:26: note: add a pair of parentheses to declare a variable TimeKeeper time_keeper(Timer()); ^ ( ) timekeeper.cc:10:21: error: member reference base type 'TimeKeeper (Timer (*)())' is not a structure or union return time_keeper.get_time(); ~~~~~~~~~~~^~~~~~~~~ SolutionsThe required interpretation of these ambiguous declarations is rarely the intended one.[4][5] Function types in C++ are usually hidden behind typedefs and typically have an explicit reference or pointer qualifier. To force the alternate interpretation, the typical technique is a different object creation or conversion syntax. In the type conversion example, there are two alternate syntaxes available for casts: the "C-style cast" // declares a variable of type int
int i((int)my_dbl);
or a named cast: int i(static_cast<int>(my_dbl));
In the variable declaration example, the preferred method (since C++11) is uniform (brace) initialization.[6] This also allows limited omission of the type name entirely: //Any of the following work:
TimeKeeper time_keeper(Timer{});
TimeKeeper time_keeper{Timer()};
TimeKeeper time_keeper{Timer{}};
TimeKeeper time_keeper( {});
TimeKeeper time_keeper{ {}};
Prior to C++11, the common techniques to force the intended interpretation were use of an extra parenthesis or copy-initialization:[5] TimeKeeper time_keeper( /*Avoid MVP*/ (Timer()) );
TimeKeeper time_keeper = TimeKeeper(Timer());
In the latter syntax, the copy-initialization is likely to be optimized out by the compiler.[7] Since C++17, this optimization is guaranteed.[8] Notes
References
External links
|