I was looking at some C++ code, and one line stood out to me for a moment while I pondered how to parse it. Was it a forward declaration for a function, or a global object being constructed.
Turns out there is an ambiguity in the C++ grammar. The problem has become know as the
Most Vexing Parse.
Consider the following code (which I don't think actually qualifies as the Most Vexing Parse, but is related):
SomeClassType maybeAnObjectMaybeAFunctionName();
Is this declaring a global object, or is this a forward declaration for a function which takes no arguments and returns an object of type SomeClassType? Many programmers might look at that an assume it's an object declaration, but it's actually a forward declare.
If you want an object declaration, omit the
():
SomeClassType maybeAnObjectMaybeAFunctionName;
The Most Vexing Parse comes in when there are arguments. In which case, how it is interpreted depends on whether the arguments are expressions or types.
SomeClassType maybeAnObjectMaybeAFunctionName(ObjectName());
Here
ObjectName is a type. In this case, you might view this as calling the constructor for that type, in which case
ObjectName() would represent an expression. However, the compiler sees this as a function signature, where
ObjectName() represents an anonymous function which takes no parameters, and returns a value of type
ObjectName. Given that interpretation, now
SomeClassType maybeAnObjectMaybeAFunctionName(ObjectName()); is also interpreted as a function signature. It is a function, which takes a pointer to a function as argument, where the argument function takes no parameters and returns a value of type
ObjectName, and the outer function returns an object of type
SomeClassType. So you have a forward declaration for a function, rather than declaring and initializing an object. This surprises a lot of people, and isn't usually what they intended, hence the name the Most Vexing Parse.
You can resolve the ambiguity by adding extra parenthesis.
SomeClassType maybeAnObjectMaybeAFunctionName((ObjectName()));
As per the grammar, type names can't have arbitrary parenthesis around them, however that's not a problem for expressions, hence
(ObjectName()) is interpreted unambiguously as an expression. Given that it's an expression, and not a type, the only possible interpretation for the argument list is then as a method call. Hence you end up defining and initializing a variable.
As a side note, an empty set of parenthesis
() is not an expression. There needs to be something inside for it to be an expression. Hence, the rule is add extra parenthesis when initializing an object with arguments, and omit the parenthesis when there are no arguments, and you'll get a variable declaration rather than a forward declaration for a function.
This is another way to avoid the ambiguity. Use Uniform Initialization Syntax:
SomeClassType maybeAnObjectMaybeAFunctionName{ObjectName{}};
The braces
{} can not be interpreted as a parameter list for a function type, and so it is unambiguously constructing and initializing a variable.
Small footnote: This ambiguity relates to using a class name as the constructor name. Hence a single identifier can be interpreted as either a type name, or a call to a function (the constructor for that type). The ambiguity is resolved in favor of the type name.