What is the most vexing parse?

Sandor Dargo - Dec 22 '21 - - Dev Community

The most vexing parse is a specific form of syntactic ambiguity resolution in the C++ programming language. The term was used by Scott Meyers in Effective STL. It is formally defined in section 8.2 of the C++ language standard.

It means that whatever that can be interpreted as a function declaration will be interpreted as a function declaration. It also means long minutes sitting in front of a failed compilation trying to figure out what the heck is going on.

Take the following example:

std::string foo();
Enter fullscreen mode Exit fullscreen mode

Probably this is the simplest form of the most vexing parse. The unsuspecting coder might think that we just declared a string called foo and called its default constructor, so initialized it as an empty string.

Then, for example, when we try to call empty() on it, and we have the following error message (with gcc):

main.cpp:18:5: error: request for member 'empty' in 'foo', 
which is of non-class type 'std::string()' 
{aka 'std::__cxx11::basic_string<char>()'
Enter fullscreen mode Exit fullscreen mode

What happened is that the above line of code was interpreted as a function declaration. We just declared a function called foo, taking no parameters and returning a string. Whereas we only wanted to call the default constructor.

This can give a kind of headache to debug even if you know about the most vexing parse. Mostly because you see the compiler error on a different line, not where you declared your varibale function, but where you try to use it.

This can be fixed very easily. You don't need to use parentheses at all to declare a variable calling its default constructor. But since C++11, if you want you can also use the {}-initialization. Both examples are going to work just fine:

std::string foo;
std::string bar{};
Enter fullscreen mode Exit fullscreen mode

Now let's have a look at a bit more interesting example:

#include <iostream>
#include <string>

struct MyInt {
    int m_i;
};

class Doubler {
public:
    Doubler(MyInt i) : my_int(i) {}

    int doubleIt() {
        return my_int.m_i*2;
    }

private:
    MyInt my_int;

};


int main() {
    int i=5;
    Doubler d(MyInt(i)); // most vexing parse here!
    std::cout << d.doubleIt() << std::endl;
}
Enter fullscreen mode Exit fullscreen mode

You might think that we initialize a Doubler class with a MyInt taking i as a parameter. But instead what we just declared is a function called d that would return a Doubler and it would take a parameter called i of type MyInt.

Hence the error message:

main.cpp: In function 'int main()':
main.cpp:25:20: error: request for member 'doubleIt' in 'd', which is of non-class type 'Doubler(MyInt)'
   25 |     std::cout << d.doubleIt() << std::endl;
      |                    ^~~~~~~~
Enter fullscreen mode Exit fullscreen mode

There are 3 ways to fix it:

  • Declare the MyInt object outside of the call, on the previous line, but then it won't be a temporary anymore.
MyInt mi(i);
Doubler d(mi); // no more most vexing parse here!
Enter fullscreen mode Exit fullscreen mode
  • Replace the any or both of the parentheses with the brace initialization. Both Doubler d{MyInt(i)}; or Doubler d(MyInt{i}) would work, just like Doubler d{MyInt{i}}. And this third one in consistent at least in how we call the constructors. The potential downside is that this only works since C++11.
// these all work
Doubler d1{MyInt(i)};
Doubler d2(MyInt{i});   
Doubler d3{MyInt{i}};
Enter fullscreen mode Exit fullscreen mode
  • If you are using an older version of C++ than C++11, you can add an extra pair of parentheses around the argument that is meant to be sent to the constructor: Doubler d((MyInt(i))). This also makes it impossible to parse it as a declaration.
Doubler d((MyInt(i))); // This also works
Enter fullscreen mode Exit fullscreen mode

Conclusion

The most vexing parse is a form of a subtle bug that most probably you faced as a beginner C++ programmer and it made you lose quite a few minutes while trying to spot the error.

The most vexing parse says that anything that can be interpreted as a function declaration will be interpreted like that. Often, when you meant to declare a local variable, the compiler takes it as a function declaration and therefore it will fail on later lines.

You can fix it in different ways and if you use at least C++11, the {}-initialization should be the easiest and most readable way to do it.

If you liked this article, I invite you to read this one too.

Connect deeper

If you liked this article, please

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .