Most vexing parse
{{Short description|Syntactic ambiguity in C++}}
{{Lead too short|date=May 2024}}
The 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 the latter.
Occurrence
The term "most vexing parse" was first used by Scott Meyers in his 2001 book Effective STL.{{cite book|last=Meyers|first=Scott|title=Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library|publisher=Addison-Wesley|year=2001|isbn=0-201-74962-9|author-link=Scott Meyers}} While unusual in C, the phenomenon was quite common in C++ until the introduction of uniform initialization in C++11.{{Cite web|last=Coffin|first=Jerry|date=29 December 2012|title=c++ - What is the purpose of the Most Vexing Parse?|url=https://stackoverflow.com/questions/14077608/what-is-the-purpose-of-the-most-vexing-parse|url-status=live|archive-url=https://archive.today/20210117023312/https://stackoverflow.com/questions/14077608/what-is-the-purpose-of-the-most-vexing-parse/14077852%2314077852|archive-date=17 January 2021|access-date=2021-01-17|website=Stack Overflow}}
Examples
= C-style casts =
A 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 i
with initial value produced by converting my_dbl
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:
// A function named i takes an integer and returns an integer.
int i(int my_dbl);
= Unnamed temporary =
A 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
- a variable definition for variable {{Code|time_keeper}} of class {{Code|TimeKeeper}}, initialized with an anonymous instance of class {{Code|Timer}} or
- a function declaration for a function {{Code|time_keeper}} that returns an object of type {{Code|TimeKeeper}} and has a single (unnamed) parameter, whose type is a (pointer to a) functionAccording 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++. taking no input and returning {{Code|Timer}} objects.
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:{{Cite web|last=Lattner|first=Chris|date=5 April 2010|title=Amazing Feats of Clang Error Recovery|url=https://blog.llvm.org/posts/2010-04-05-amazing-feats-of-clang-error-recovery/|url-status=dead|archive-url=https://web.archive.org/web/20200926165432/https://blog.llvm.org/posts/2010-04-05-amazing-feats-of-clang-error-recovery/|archive-date=26 September 2020|access-date=2021-01-17|website=LLVM Project Blog|at=The Most Vexing Parse}}
{{codett|2=console|$ clang++ time_keeper.cc}}
timekeeper.cc:9:25: {{fontcolor|magenta|warning:}} parentheses were disambiguated as a function declaration
[-Wvexing-parse]
{{codett|2=cpp|TimeKeeper time_keeper(Timer());}}
{{fontcolor|green|^
timekeeper.cc:9:26: note: add a pair of parentheses to declare a variable
{{codett|2=cpp|TimeKeeper time_keeper(Timer());}}
{{fontcolor|green|^}}
{{fontcolor|green|( )}}
timekeeper.cc:10:21: {{fontcolor|red|error:}} member reference base type '{{codett|2=cpp|TimeKeeper (Timer (*)())}}' is not a
structure or union
{{codett|2=cpp|return time_keeper.get_time();}}
{{fontcolor|green|
Solutions
The required interpretation of these ambiguous declarations is rarely the intended one.{{Cite web|last1=DrPizza|last2=Prototyped|last3=wb|last4=euzeka|last5=Simpson|first5=Homer J|date=October 2002|title=C++'s "most vexing parse"|url=https://arstechnica.com/civis/viewtopic.php?f=20&t=767929|url-status=live|archive-url=https://archive.today/20150520052216/http://arstechnica.com/civis/viewtopic.php?f=20&t=767929|archive-date=20 May 2015|access-date=2021-01-17|website=ArsTechnica OpenForum}}{{Cite web|last=Boccara|first=Jonathan|date=2018-01-30|title=The Most Vexing Parse: How to Spot It and Fix It Quickly|url=https://www.fluentcpp.com/2018/01/30/most-vexing-parse/|access-date=2021-01-17|website=Fluent C++|language=en-US|archive-date=2021-11-25|archive-url=https://web.archive.org/web/20211125171053/https://www.fluentcpp.com/2018/01/30/most-vexing-parse/|url-status=live}} 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
In the variable declaration example, the preferred method (since C++11) is uniform (brace) initialization.{{Cite web|last=Stroustrup|first=Bjarne|date=19 August 2016|title=C++11 FAQ|url=https://www.stroustrup.com/C++11FAQ.html#uniform-init|access-date=2021-01-17|website=www.stroustrup.com|at=Uniform initialization syntax and semantics|language=en|archive-date=2021-08-20|archive-url=https://web.archive.org/web/20210820103659/https://www.stroustrup.com/C++11FAQ.html#uniform-init|url-status=live}} 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:
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.{{Cite web|last=|first=|date=|title=Myths and urban legends about C++|url=https://isocpp.org/wiki/faq/myths#copy-elision|url-status=live|archive-url=https://archive.today/20210117033637/https://isocpp.org/wiki/faq/myths%23copy-elision|archive-date=17 January 2021|access-date=2021-01-17|website=C++ FAQ|at=What is copy elision? What is RVO?}} Since C++17, this optimization is guaranteed.{{Cite web|last=Devlieghere|first=Jonas|date=2016-11-21|title=Guaranteed Copy Elision|url=https://jonasdevlieghere.com/guaranteed-copy-elision/|access-date=2021-01-17|website=Jonas Devlieghere|language=en|archive-date=2021-11-25|archive-url=https://web.archive.org/web/20211125171053/https://jonasdevlieghere.com/guaranteed-copy-elision/|url-status=live}} Note, however, the caveats covered in {{Cite web|last=Brand|first=C++|date=2018-12-11|title=Guaranteed Copy Elision Does Not Elide Copies|url=https://devblogs.microsoft.com/cppblog/guaranteed-copy-elision-does-not-elide-copies/|access-date=2021-01-17|website=Microsoft C++ Team Blog|language=en-US|archive-date=2021-11-25|archive-url=https://web.archive.org/web/20211125171050/https://devblogs.microsoft.com/cppblog/guaranteed-copy-elision-does-not-elide-copies/|url-status=live}}
Notes
References
{{reflist}}
External links
- Discussion in the C++03 standard final draft (see §8.2 Ambiguity resolution [dcl.ambig.res]): https://web.archive.org/web/20141113085328/https://cs.nyu.edu/courses/fall11/CSCI-GA.2110-003/documents/c++2003std.pdf
- CppReference on direct initialization (the sort vulnerable to the most vexing parse): https://en.cppreference.com/w/cpp/language/direct_initialization {{Webarchive|url=https://web.archive.org/web/20211213184451/https://en.cppreference.com/w/cpp/language/direct_initialization |date=2021-12-13 }}
{{C++ programming language}}