It is often recommended that class objects be initialized using direct constructors rather than assignment. [Meyers 01] Direct constructors avoids construction, copying, and destruction of a temporary copy of the object. To wit, object should be constructed this way:
Code Block | ||||
---|---|---|---|---|
| ||||
Widget w( /* constructor arguments */);
|
rather than this way:
Code Block | ||||
---|---|---|---|---|
| ||||
Widget w = Widget( /* constructor arguments */);
|
or this way (for classes that support this syntax)
Code Block | ||||
---|---|---|---|---|
| ||||
Widget w = /* constructor argument */;
|
Besides being inefficient, this last syntax violates OOP09-CPP. Ensure that single-argument constructors are marked "explicit".
However, C++ parsers are often liable to misparsing constructor arguments. While compilers will often generate a compiler error upon such misparses, it is possible for such misparses to slip past a compiler and lurk in executable code, with unexpected results.
Non-Compliant Code Example
possible to devise syntax which can ambiguously be interpreted as either an expression statement or a declaration. Syntax of this sort is referred to as a vexing parse because the compiler must use disambiguation rules to determine the semantic results. The C++ Standard, [stmt.ambig], paragraph 1, states in part [ISO/IEC 14882-2014]:
There is an ambiguity in the grammar involving expression-statements and declarations: An expression-statement with a function-style explicit type conversion as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a
(
. In those cases the statement is a declaration. [Note: To disambiguate, the whole statement might have to be examined to determine if it is an expression-statement or a declaration. ...
A similarly vexing parse exists within the context of a declaration where syntax can be ambiguously interpreted as either a function declaration, or a declaration with a function-style cast as the initializer. The C++ Standard, [dcl.ambig.res], paragraph 1, states in part:
The ambiguity arising from the similarity between a function-style cast and a declaration mentioned in 6.8 can also occur in the context of a declaration. In that context, the choice is between a function declaration with a redundant set of parentheses around a parameter name and an object declaration with a function-style cast as the initializer. Just as for the ambiguities mentioned in 6.8, the resolution is to consider any construct that could possibly be a declaration a declaration.
Do not write a syntactically semantically ambiguous declaration, including vexing parses. With the advent of uniform initialization syntax using a braced-init-list, there is now syntax that unambiguously specifies a declaration instead of an expression statement. Declarations can also be disambiguated by using nonfunction-style casts, initialization using =
, or by removing extraneous parenthesis around the parameter name.
Noncompliant Code Example
In this noncompliant code example, an attempt is made to declare a local variable, w
, of type Widget
while executing the default constructor. However, this is syntactically ambiguous where the code could either be a declaration of a function pointer accepting no arguments and returning a Widget
, or a declaration of a local variable of type Widget
. In this non-compliant example, the class Widget
has a default constructor.
Code Block | ||||
---|---|---|---|---|
| ||||
class#include <iostream> struct Widget { public: explicit Widget() {cerr std::cout << "constructedConstructed" << std::endl;} }; int void mainf() { Widget w(); return 0; } |
However, while a human may consider w
to be explicitly built with the default constructor, the compiler interprets w
to be a pointer to a function that takes no arguments, and returns a Widget
!
As a result, this program compiles and prints no output, because the default constructor is never actually invoked.
Compliant Solution
This situation is ameliorated by removing the parentheses after w
.
...
Running this program produces the single output constructed
.
Non-Compliant Code Example
Here is a more complex non-compliant example. The class Widget
maintains a single int
, and the class Gadget
maintains a single Widget
.
...
As a result, this program compiles cleanly and prints only 3
as output, because no Gadget
or Widget
is constructed.
Compliant Solution
This situation is ameliorated by moving the Widget
construction outside Gadget
.
...
widget constructed
gadget constructed
3
Risk Assessment
Not guarding implicit constructor parsing could lead to unexpected behavior.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
OOP31-CPP | low | likely | low | P9 | L2 |
Automated Detection
Tool | Version | Checker | Description | ||||||
| 2510 |
Bibliography
- [Meyers 01] Item 6: Be alert for C++'s most vexing parse.
...