Jonathan Boccara's blog

The Most Vexing Parse: How to Spot It and Fix It Quickly

Published January 30, 2018 - 10 Comments

Daily C++

Everyone has their little defaults. You know, that little something that they do from time to time and that gets on your nerves, even though they’re otherwise nice people?

For C++, one of these little annoyances is the most vexing parse, well, as its name suggests.

I think this is a topic related to expressiveness of code, because it’s a case where the code doesn’t do what it appears to do at first. However, the most vexing parse doesn’t go too far into the product because it causes a compilation error (which is the vexing part).

When you don’t know about the most vexing parse you can waste a lot of time because of it. But what make the most vexing parse particularly obnoxious is that it can make you waste time even if you know about it. I’ve known it for years and I spent 15 minutes of not the best time of my life chasing after a mysterious compile error the other day, only to find our most vexing friend lurking happily beneath it.

And then I looked for ways to identify it more quickly. So I’m writing this article to help you track it down and root it out of your code, without spending too much time on it.

The Most Vexing Parse

If you already know what the most vexing parse is about, you can skip over to the next section.

The expression was coined by Scott Meyers, who talks about it in details in Item 6 of Effective STL. It comes from a rule in C++ that says that anything that could be considered as a function declaration, the compiler should parse it as a function declaration. And even if such an expression could be interpreted as something else. And even if this something else would seems a lot more natural to a programmer.

For instance, consider the following code:

struct B
{
    explicit B(int x){}
};

struct A
{
    A (B const& b){}
    void doSomething(){}
};

int main()
{    
    int x = 42;

    A a(B(x));
    
    a.doSomething();
}

This code does not compile.

Indeed, the following line:

A a(B(x));

is interpreted as a function declaration: it would be a function called a, that takes by value a parameter of type B called x and that returns an object of type A by value.

In fact, it could be even more fun: if B has a default constructor then the expression:

A a(B());

is parsed as a function that returns an A and that takes a function that returns a B and takes no parameter. How fun is that?

most vexing parse

And what makes it difficult to diagnose is that the compilation error occurs a different line:

a.doSomething();

Indeed, this line doesn’t make sense since we can’t call doSomething on the function a.

Thanks to Patrice Roy for his advice on picking use cases and to David Forgeas for noticing the fun case.

A degenerate case

Here is a simpler case where the code doesn’t compile, that you probably have come across at some point:

struct A
{
    void doSomething(){}
};
 
int main()
{    
    A a();
    
    a.doSomething();
}

This code does not compile because the line

A a();

is interpreted as a function declaration: it would be a function called a, that takes no parameters (hence the empty parentheses) and that return an object of type A by value.

How vexing is that?

A vexation in action

Let’s take a larger example, which was provided to me by my colleague and friend Jonathan and which I thank for such a great example. I’ve trimmed it down a bit so that it doesn’t take you long to read.

What makes this example interesting is that the most vexing parse is hidden into code that looks perfectly reasonable. There is a compilation error on line 38. Can you see the problem?

I do suggest that you make the effort to search for it right now. It’s good practice that will make you more acute to analyse this type of compilation error.

#include <map>

class Date
{
public:
   Date(int year, int month, int day);
   // ...
};

using FutureDate = Date;   // the original example used NamedType here
using OptionExpiry = Date; // but I removed it for simplification

class KeyInstrument
{
public:
   KeyInstrument(const FutureDate & futureDate, const OptionExpiry & optionExpiry);
   bool operator<(const KeyInstrument &other) const;
   // ...
};

enum class StatusInstrument
{
    Ok,
    NegativeFwdFwdVol
};

using PairStatus = std::pair<KeyInstrument, StatusInstrument>;
using StatusCalib = std::map<KeyInstrument, StatusInstrument>;

int main()
{
    Date date1(2017, 12, 02);
    Date date2(2018, 03, 30);

    KeyInstrument key(FutureDate(date1), OptionExpiry(date2));

    StatusCalib status;
    status.insert(PairStatus(key, StatusInstrument::Ok));
}

The root cause of the problem is in fact happening on line 35, in that line of code that looks completely innocuous:

KeyInstrument key(FutureDate(date1), OptionExpiry(date2));

In fact it could be parsed as a function. This function would be called key and take 2 parameters: a FutureDate parameter, called date1 (the surrounding parentheses don’t matter here), and an OptionExpiry parameter called date2. And it doesn’t matter that date1 and date2 also happen to be local variables in main. The names of the parameters are local to the function declaration and hide the variables outside.

Annoying, isn’t it?

Now let’s see the tools we have to quickly detect the most vexing parse, and how to fix that code.

The blood trail of the most vexing parse

clang

clang provides by far the most elegant detection mechanism that I have found. The compiler emits a warning, -Wvexing-parse, when it sees you fall into the trap of the most vexing parse. It’s as simple as that.

I looked around but couldn’t find documentation about how exactly this warning performs its check. From what I’ve experimented, it seems to fire off whenever there is a function declaration inside of another function.

So the annoying analysis of this compilation error doesn’t even start, since the compiler pinned it down for you, and displays it in its output:

main.cpp:34:22: error: parentheses were disambiguated as a function declaration [-Werror,-Wvexing-parse]
    KeyInstrument key(FutureDate(date1), OptionExpiry(date2));
                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

How nice is that?

gcc

For gcc, I couldn’t find any such warning, but in some cases there you can find a hint in the error message. Here what gcc (7.2.0) outputs when fed our example code:

main.cpp: In function 'int main()':
main.cpp:37:55: error: no matching function for call to 'std::pair<KeyInstrument, StatusInstrument>::pair(KeyInstrument (&)(FutureDate, OptionExpiry), StatusInstrument)'
     status.insert(PairStatus(key, StatusInstrument::Ok));
                                                       ^

Not really informative. But there is one thing worth noting here: the (&), in the second line of the message. It doesn’t occur that often in error messages, but it’s easy to overlook. This symbol means that the error is about a function being misused. That’s a hint for the most vexing parse.

Okay, it’s a small hint, a heuristic rather than anything else, but it can point you to the right direction. So my advice to you is this: if you don’t immediately understand a compilation error, and you see (&) inside it, think about the most vexing parse. It could be something else, but it could also be this. And the hardest thing about the most vexing parse it to think about it, not to fix it. Knowing this would have saved me 15 minutes of my time to do something more enjoyable than hunting down a most vexing parse.

However the (&) doesn’t appear in all the compilation errors related to the most vexing parse with gcc. The first example of this post doesn’t trigger it for example:

struct A
{
    void doSomething(){}
};
 
int main()
{    
    A a();
    
    a.doSomething();
}

And I couldn’t find anything that could help diagnose it faster with MSVC.

How to fix the most vexing parse

So now you know what the most vexing parse is, and you also know some ways to track it down. Let’s see how to fix it now, and make our code compile.

In C++11

C++11 brings uniform initialization, which consists in calling constructors with curly braces {} instead of parentheses (). Applied to our example, it gives the following result:

KeyInstrument key(FutureDate{date1}, OptionExpiry{date2});

There is no longer an ambiguity. A function doesn’t declare its parameters with curly braces, so there is no way the compiler could parse this as a function. Therefore this is parsed as a construction of an object, key, like we would have expected in the first place.

In fact, if you always use uniform initialization, the most vexing parse never happens. But whether to code like this or not is a whole topic, discussed in details in Item 7 of Effective Modern C++.

Another possibility (and thanks Christophe for showing this to me) is to use the “auto to stick” syntax:

auto key = KeyInstrument(FutureDate(date1), OptionExpiry(date2));

I’m also preparing an article on the “auto to stick” syntax, that should be released in the next few weeks.

Before C++11

When uniform initialization wasn’t available yet, the solution to fix the most vexing parse was to add an extra pair of parentheses around one of the arguments to the constructor:

KeyInstrument key((FutureDate(date1)), OptionExpiry(date2));

This makes the expression impossible to parse as a function declaration. But this isn’t as elegant as the solution with the uniform initialization.

And when there is no parameters to surround, just omit all the parentheses:

A a;

Together we’ll beat it, stopped being vexed, and live a happier life.

Don't want to miss out ? Follow:   twitterlinkedinrss
Share this post!Facebooktwitterlinkedin

Comments are closed