Most vexing parse
Most vexing parse refers to one particularly obtuse implication of the C++ specification which may incur high levels of confusion when fighting one's compiler due to the C++ standard choosing a grammar ambiguity resolution that favours a lesser-used language feature over a much more common one. Parse in its noun form refers to the compiler's interpretation when analyzing source code.
More specifically, the C++ grammar cannot in general distinguish between a parameter being passed to an object's constructor and the specification of a function's type, with a compliant C++ compiler always swaying towards the latter.
C has no concept of object creation and thus unvexingly always parses function type declaration. C++ introduced syntax for object creation that inadvertantly coincides with function type declaration in some cases. Due to C++'s backwards compatibility requirements, it is forced to choose the vexing parse.
The term was first used by Scott Meyers in his 2001 book Effective STL.[1] The phenomenon was quite common in C++ until the introduction of uniform initialization in C++11, which introduces an alternative syntax for object initialization using braces {} instead of parentheses (), circumnavigating the syntactical ambiguity.[2]
Examples
[edit | edit source]C-style cast
[edit | edit source]A simple example appears when a functional cast is intended to convert an expression for initializing a variable:
Line 3 above is ambiguous. One possible interpretation is to declare a variable i with initial value produced by converting x to an int. However, C allows superfluous parentheses around function parameter declarations; in this case, the declaration of i is instead a function declaration equivalent to the following:
creation
Nullary constructor
[edit | edit source]#include <iostream>
struct X {
// fieldless
int f() {
return 5;
}
};
int main() {
X x();
std::cout << x.f() << std::endl;
}
As it stands, the above neither constitutes a valid C nor a valid C++ program. Line 12 may be changed to X x{}; to avoid MVP.
Unnamed temporary
[edit | edit source]A more elaborate example is:
class Timer {
// ...
};
class TimeKeeper {
public:
explicit TimeKeeper(Timer t);
int getTime();
};
int main() {
TimeKeeper time_keeper(Timer());
return time_keeper.get_time();
}
Line 12 above is ambiguous: it could be interpreted either as
- a variable definition for variable
time_keeperof classTimeKeeper, initialized with an anonymous instance of classTimeror - a function declaration for a function
time_keeperthat returns an object of typeTimeKeeperand has a single (unnamed) parameter, whose type is a (pointer to a) function[Note 1] taking no input and returningTimerobjects.
The C++ standard requires the second interpretation, which is inconsistent with the subsequent line 13. For example, Clang++ warns that the most vexing parse has been applied on line 12 and errors on the subsequent line 13:[3]
$ clang++ timekeeper.cctimekeeper.cc:12:27: warning: parentheses were disambiguated as a function declaration [-Wvexing-parse]TimeKeeper time_keeper(Timer());^~~~~~~~~ timekeeper.cc:12:28: note: add a pair of parentheses to declare a variableTimeKeeper time_keeper(Timer());^ ( ) timekeeper.cc:13:23: error: member reference base type 'TimeKeeper (Timer (*)())' is not a structure or unionreturn time_keeper.get_time();~~~~~~~~~~~^~~~~~~~~ 1 warning and 1 error generated.
Solutions
[edit | edit source]The 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"
// A variable of type int is declared.
int i((int)x);
or a named cast:
int i(static_cast<int>(x));
Another syntax, also valid in C, is to use = when initializing variables:
int i = int(x);
In the variable declaration example, an alternate 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
[edit | edit source]- ^ According to C++ type decay rules, a function object declared as a parameter is equivalent to a pointer to a function of that type. See Function object#In C and C++.
References
[edit | edit source]- ^ Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- ^ Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- ^ Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- ^ Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- ^ a b Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- ^ Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- ^ Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- ^ Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value). Note, however, the caveats covered in Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
External links
[edit | edit source]- Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).
- Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).: Discussion in the C++03 standard final draft
- Lua error in Module:Citation/CS1/Configuration at line 2172: attempt to index field '?' (a nil value).: the sort vulnerable to the most vexing parse.