It is possible to devise syntax which that can ambiguously be interpreted as either an expression statement or a declaration. Syntax of this sort is referred to as called 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], in part, states the following:
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, states the following:
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 ambiguous declaration, including vexing parses. With the advent of uniform initialization syntax using a braceda 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 by initializing using =, or by removing extraneous parenthesis around the parameter name.
Noncompliant Code Example
In this noncompliant code example, an anonymous local variable of type std::unique_lock
is expected to lock and unlock the mutex m
by virtue of RAII. However, the declaration is syntactically ambiguous as it can be interpreted as declaring an anonymous object and calling its single-argument converting constructor or interpreted as declaring an object named m
and default constructing it. The syntax used in this example defines the latter instead of the former, and so the mutex object is never locked.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <mutex>
static std::mutex m;
static int shared_resource;
void increment_by_42() {
std::unique_lock<std::mutex>(m);
shared_resource += 42;
} |
Compliant Solution
In this compliant solution, the lock object is given an identifier (other than m
) and the proper converting constructor is called.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <mutex>
static std::mutex m;
static int shared_resource;
void increment_by_42() {
std::unique_lock<std::mutex> lock(m);
shared_resource += 42;
} |
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 declaration is syntactically ambiguous where the code could be 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
. The syntax used in this example defines the former instead of the latter.
...
As a result, this program compiles and prints no output , because the default constructor is never actually invoked.
Compliant Solution
This compliant solution shows two equally - compliant ways to write the declaration. The first way is to elide the parenthesis parentheses after the variable declaration; this , which ensures the syntax is that of a variable declaration instead of a function declaration. The second way is to use a braced-init-list to direct-initialize the local variable.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> struct Widget { Widget() { std::cout << "Constructed" << std::endl; } }; void f() { Widget w1; // Elide the parenthesisparentheses Widget w2{}; // Use direct initialization } |
Running this program produces the output : Constructed
Constructed
twice, once for w1
and once for w2
.
Noncompliant Code Example
This noncompliant code example demonstrates a vexing parse. The declaration Gadget g(Widget(i));
is not parsed as declaring a Gadget
object with a single argument, but . It is instead parsed as a function declaration with a redundant set of parenthesis parentheses around a parameter.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> struct Widget { explicit Widget(int Ii) { std::cout << "Widget constructed" << std::endl; } }; struct Gadget { explicit Gadget(Widget wid) { std::cout << "Gadget constructed" << std::endl; } }; void f() { int i = 3; Gadget g(Widget(i)); std::cout << i << std::endl; } |
Parentheses around parameter names are optional, so the following is a semantically identical spelling of the declaration:.
Code Block | ||||
---|---|---|---|---|
| ||||
Gadget g(Widget i); |
As a result, this program is well-formed and prints only 3
as output , because no Gadget
or Widget
objects are constructed.
Compliant Solution
This compliant solution demonstrates two equally - compliant ways to write the declaration of g
. The first declaration, g1
, uses an extra set of parenthesis parentheses around the argument to the constructor call, forcing the compiler to parse this it as a local variable declaration of type Gadget
instead of as a function declaration. The second declaration, g2
, uses direct initialization to similar effect.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <iostream> struct Widget { explicit Widget(int Ii) { std::cout << "Widget constructed" << std::endl; } }; struct Gadget { explicit Gadget(Widget wid) { std::cout << "Gadget constructed" << std::endl; } }; void f() { int i = 3; Gadget g1((Widget(i))); // Use extra parenthesisparentheses Gadget g2{Widget(i)}; // Use direct initialization std::cout << i << std::endl; } |
Running this program produces the expected output:.
Code Block | ||
---|---|---|
| ||
Widget constructed |
...
Gadget constructed |
...
Widget constructed |
...
Gadget constructed |
...
3 |
Risk Assessment
Syntactically ambiguous declarations can lead to unexpected program execution. However, it is likely that rudimentary testing would uncover violations of this rule.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
DCL53-CPP | Low | Unlikely | Medium | P2 | L3 |
Automated Detection
Tool | Version | Checker | Description |
---|
2510
CodeSonar |
| LANG.STRUCT.DECL.FNEST | Nested Function Declaration | ||||||
Helix QAC |
| C++1109, C++2510 | |||||||
Klocwork |
| CERT.DCL.AMBIGUOUS_DECL | |||||||
LDRA tool suite |
| 296 S | Partially implemented | ||||||
Parasoft C/C++test |
| CERT_CPP-DCL53-a | Parameter names in function declarations should not be enclosed in parentheses | ||||||
Polyspace Bug Finder |
| CERT C++: DCL53-CPP | Checks for declarations that can be confused between:
Rule fully covered. |
Clang |
| -Wvexing-parse |
SonarQube C/C++ Plugin |
| S3468 |
Related Vulnerabilities
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
...
Bibliography
[ISO/IEC 14882-2014] | Subclause 6.8, "Ambiguity |
Resolution" Subclause 8.2, "Ambiguity |
Resolution" |
[Meyers |
2001] | Item 6, "Be |
Alert for C++'s |
...
Most Vexing Parse" |
...