Most vexing parse

Last updated

The most vexing parse is a counterintuitive ambiguity resolution in C and C++. In certain situations, the C/C++ grammar cannot distinguish between initializing an object parameter and declaring a function while specifying the function's return type. In these situations, the compiler is required to interpret the line as the latter, even though this is rarely the programmer's intention.

Contents

The problem originates from backward compatibility constraints imposed by the need for C++ to be a superset of C. C has no concept of object creation, and thus will always parse the code as a function declaration. C++ introduced syntax for object creation that inadvertently coincides with function type declaration in some cases.[ citation needed ]

The term was first used by Scott Meyers in his 2001 book Effective STL . [1] It was a common problem for C++ versions prior to C++11, which introduced an alternative syntax called uniform initialization that uses braces {} instead of parentheses (), avoiding the syntactic ambiguity. [2]

Examples

C-style cast

A simple example appears when a functional cast is intended to convert an expression for initializing a variable:

  voidf(doublex){  // An integer named i is assigned a C-style-cast value.  inti(int(x));  }

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:

  // A function named i takes an integer and returns an integer.  inti(intx);

Unnamed temporary

A more elaborate example is:

  classTimer{  // ...  };    classTimeKeeper{  public:  explicitTimeKeeper(Timert);  intgetTime();  };    intmain(){  TimeKeepertime_keeper(Timer());  returntime_keeper.get_time();  }

Line 12 above is ambiguous: it could be interpreted either as

  1. a variable definition for variable time_keeper of class TimeKeeper, initialized with an anonymous instance of class Timer or
  2. a function declaration for a function time_keeper that returns an object of type TimeKeeper and has a single (unnamed) parameter, whose type is a (pointer to a) function [Note 1] taking no input and returning Timer objects.

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]TimeKeepertime_keeper(Timer());^~~~~~~~~timekeeper.cc:12:28: note: add a pair of parentheses to declare a variable   TimeKeepertime_keeper(Timer());^(      )timekeeper.cc:13:23: error: member reference base type 'TimeKeeper(Timer(*)())' is not a structure or unionreturntime_keeper.get_time();~~~~~~~~~~~^~~~~~~~~ 1 warning and 1 error generated.

Solutions

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.inti((int)x);

or a named cast:

inti(static_cast<int>(x));

Another syntax, also valid in C, is to use = when initializing variables:

inti=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:TimeKeepertime_keeper(Timer{});TimeKeepertime_keeper{Timer()};TimeKeepertime_keeper{Timer{}};TimeKeepertime_keeper({});TimeKeepertime_keeper{{}};

Prior to C++11, the common techniques to force the intended interpretation were use of an extra parenthesis or copy-initialization: [5]

TimeKeepertime_keeper(/*Avoid MVP*/(Timer()));TimeKeepertime_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

  1. 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

  1. Meyers, Scott (2001). Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library. Addison-Wesley. ISBN   0-201-74962-9.
  2. Coffin, Jerry (29 December 2012). "c++ - What is the purpose of the Most Vexing Parse?". Stack Overflow. Archived from the original on 17 January 2021. Retrieved 2021-01-17.
  3. Lattner, Chris (5 April 2010). "Amazing Feats of Clang Error Recovery". LLVM Project Blog. The Most Vexing Parse. Archived from the original on 26 September 2020. Retrieved 2021-01-17.
  4. DrPizza; Prototyped; wb; euzeka; Simpson, Homer J (October 2002). "C++'s "most vexing parse"". ArsTechnica OpenForum. Archived from the original on 20 May 2015. Retrieved 2021-01-17.
  5. 1 2 Boccara, Jonathan (2018-01-30). "The Most Vexing Parse: How to Spot It and Fix It Quickly". Fluent C++. Archived from the original on 2021-11-25. Retrieved 2021-01-17.
  6. Stroustrup, Bjarne (19 August 2016). "C++11 FAQ". www.stroustrup.com. Uniform initialization syntax and semantics. Archived from the original on 2021-08-20. Retrieved 2021-01-17.
  7. "Myths and urban legends about C++". C++ FAQ. What is copy elision? What is RVO?. Archived from the original on 17 January 2021. Retrieved 2021-01-17.
  8. Devlieghere, Jonas (2016-11-21). "Guaranteed Copy Elision". Jonas Devlieghere. Archived from the original on 2021-11-25. Retrieved 2021-01-17. Note, however, the caveats covered in Brand, C++ (2018-12-11). "Guaranteed Copy Elision Does Not Elide Copies". Microsoft C++ Team Blog. Archived from the original on 2021-11-25. Retrieved 2021-01-17.