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.
...