IssueThis 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
.
RiskIn 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.
FixMake 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:
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);
Example – Function or Object Declarationclass ResourceType {
int aMember;
public:
int getMember();
};
void getResource() {
ResourceType aResource(); //Noncompliant
}
In this example, aResource
might be used as an object but the
declaration syntax indicates a function declaration.
Correction — Use {} for Object DeclarationOne possible correction (after C++11) is to use braces for object declaration.
class ResourceType {
int aMember;
public:
int getMember();
};
void getResource() {
ResourceType aResource{};
}
Example – Unnamed Object or Unnamed Function Parameter Declarationclass MemberType {};
class ResourceType {
MemberType aMember;
public:
ResourceType(MemberType m) {aMember = m;}
int getMember();
};
void getResource() {
ResourceType aResource(MemberType()); //Noncompliant
}
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 DeclarationOne 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()};
}
Example – Unnamed Object or Named Function Parameter Declarationclass Integer {
int aMember;
public:
Integer(int d) {aMember = d;}
int getMember();
};
int aInt = 0;
void foo(){
Integer aInteger(Integer(aInt)); //Noncompliant
}
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 DeclarationOne 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 DeclarationIf 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();
};
Integer aInteger(Integer aInt);