Main Content

Ambiguous declaration syntax

Declaration syntax can be interpreted as object declaration or part of function declaration

Description

This defect occurs when it is not clear from a declaration whether an object declaration or function/parameter declaration is intended. The ambiguity is often referred to as most vexing parse.

For instance, these declarations are ambiguous:

  • ResourceType aResource();

    It is not immediately clear if aResource is a function returning a variable of type ResourceType or an object of type ResourceType.

  • TimeKeeper aTimeKeeper(Timer());

    It is not immediately clear if aTimeKeeper is an object constructed with an unnamed object of type Timer or a function with an unnamed function pointer type as parameter. The function pointer refers to a function with no argument and return type Timer.

The checker does not flag ambiguous declarations with global scope. For instance, the analysis does not flag declarations with global scope using the format Type a() where Type is a class type with a default constructor. The analysis interprets a as a function returning the type Type.

Risk

In case of an ambiguous declaration, the C++ Standard chooses a specific interpretation of the syntax. For instance:

  • ResourceType aResource();
    is interpreted as a declaration of a function aResource.

  • TimeKeeper aTimeKeeper(Timer());
    is interpreted as a declaration of a function aTimeKeeper with an unnamed parameter of function pointer type.

If you or another developer or code reviewer expects a different interpretation, the results can be unexpected.

For instance, later you might face a compilation error that is difficult to understand. Since the default interpretation indicates a function declaration, if you use the function as an object, compilers might report a compilation error. The compilation error indicates that a conversion from a function to an object is being attempted without a suitable constructor.

Fix

Make the declaration unambiguous. For instance, fix these ambiguous declarations as follows:

  • ResourceType aResource();

    Object declaration:

    If the declaration refers to an object initialized with the default constructor, rewrite it as:

    ResourceType aResource;
    prior to C++11, or as:
    ResourceType aResource{};
    after C++11.

    Function declaration:

    If the declaration refers to a function, use a typedef for the function.

    typedef ResourceType(*resourceFunctionType)();
    resourceFunctionType aResource;

  • TimeKeeper aTimeKeeper(Timer());

    Object declaration:

    If the declaration refers to an object aTimeKeeper initialized with an unnamed object of class Timer, add an extra pair of parenthesis:

    TimeKeeper aTimeKeeper( (Timer()) );
    prior to C++11, or use braces:
    TimeKeeper aTimeKeeper{Timer{}};
    after C++11.

    Function declaration:

    If the declaration refers to a function aTimeKeeper with a unnamed parameter of function pointer type, use a named parameter instead.

    typedef Timer(*timerType)();
    TimeKeeper aTimeKeeper(timerType aTimer);

Examples

expand all

class ResourceType {
      int aMember;
    public:
      int getMember();
};

void getResource() {
    ResourceType aResource();
}

In this example, aResource might be used as an object but the declaration syntax indicates a function declaration.

Correction — Use {} for Object Declaration

One possible correction (after C++11) is to use braces for object declaration.

class ResourceType {
      int aMember;
    public:
      int getMember();
};

void getResource() {
    ResourceType aResource{};
}
class MemberType {};

class ResourceType {
      MemberType aMember;
    public:
      ResourceType(MemberType m) {aMember = m;}
      int getMember();
};

void getResource() {
    ResourceType aResource(MemberType()); 
}

In this example, aResource might be used as an object initialized with an unnamed object of type MemberType but the declaration syntax indicates a function with an unnamed parameter of function pointer type. The function pointer points to a function with no arguments and type MemberType.

Correction — Use {} for Object Declaration

One possible correction (after C++11) is to use braces for object declaration.

class MemberType {};

class ResourceType {
      MemberType aMember;
    public:
      ResourceType(MemberType m) {aMember = m;}
      int getMember();
};

void getResource() {
    ResourceType aResource{MemberType()};
}
class Integer {
	int aMember;
public:
	Integer(int d) {aMember = d;}
	int getMember();
};

int aInt = 0;
void foo(){
	Integer aInteger(Integer(aInt));
}

In this example, aInteger might be an object constructed with an unnamed object Integer(aInt) (an object of class Integer which itself is constructed using the variable aInt). However, the declaration syntax indicates that aInteger is a function with a named parameter aInt of type Integer (the superfluous parenthesis is ignored).

Correction — Use of {} for Object Declaration

One possible correction (after C++11) is to use {} for object declaration.

class Integer {
	int aMember;
public:
	Integer(int d) {aMember = d;}
	int getMember();
};

int aInt = 0;
void foo(){
	Integer aInteger(Integer{aInt});
}
Correction — Remove Superfluous Parenthesis for Named Parameter Declaration

If aInteger is a function with a named parameter aInt, remove the superfluous () around aInt.

class Integer {
	int aMember;
public:
	Integer(int d) {aMember = d;}
	int getMember();
};

int aInt = 0;
void foo(){
	Integer aInteger(Integer aInt);
}

Result Information

Group: Good practice
Language: C++
Default: Off
Command-Line Syntax: MOST_VEXING_PARSE
Impact: Low

Version History

Introduced in R2019a