Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

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 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 semantically ambiguous 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
bgColor#FFCCCC
langcpp
#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
bgColor#ccccff
langcpp
#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.

Code Block
bgColor#FFCCCC
langcpp
#include <iostream>
 
struct Widget {
  Widget() { std::cout << "Constructed" << std::endl; }
};

 
void f() {
  Widget w();
}

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 wcompliant solution shows two equally compliant ways to write the declaration. The first way is to elide the parentheses after the variable declaration, 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
bgColor#ccccff
langcpp
class#include <iostream>
 
struct Widget {
public:
  explicit Widget() {cerr std::cout << "constructedConstructed" << std::endl; }
};

intvoid mainf() {
  Widget w;w1; // Elide the parentheses
  returnWidget 0;w2{}; // Use direct initialization
}

Running this program produces the single output constructed.

Non-Compliant Code Example

output 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. It is instead parsed as a function declaration with a redundant set of parentheses around a parameter. Here is a more complex non-compliant example. The class Widget maintains a single int, and the class Gadget maintains a single Widget.

Code Block
bgColor#FFCCCC
langcpp
class#include <iostream>

struct Widget {
public:
  explicit Widget(int in) : i(in) {cerr std::cout << "widgetWidget constructed" << std::endl;}
private:
  int i; }
};

classstruct Gadget {
public:
  explicit Gadget(Widget wid) { std:: w(wid) {cerr cout << "gadgetGadget constructed" << std::endl; }
private:
  Widget w;
};

intvoid mainf() {
  int i = 3;
  Gadget g(Widget(i));
  std::cout << i << endl;
  return 0std::endl;
}

The declaration of g is not parsed as a Gadget with a 1-argument constructor. It is instead parsed as a pointer to a function that takes a single Widget argument, called i, and returns a Gadget. For illustrative purposes, keep in mind that in a function declaration, parentheses around argument names are optional. So the following is a legitimate function declaration, and indicates how the compiler sees the above declaration:Parentheses around parameter names are optional, so the following is a semantically identical spelling of the declaration.

Code Block
bgColor#ccccff
langcpp
Gadget g(Widget i);

As a result, this program compiles cleanly is well-formed and prints only 3 as output , because no Gadget or Widget is  objects are constructed.

Compliant Solution

This situation is ameliorated by moving the Widget construction outside Gadget.compliant solution demonstrates two equally compliant ways to write the declaration of g. The first declaration, g1, uses an extra set of parentheses around the argument to the constructor call, forcing the compiler to parse 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
bgColor#ccccff
langcpp
#include <iostream>

struct Widget {
  explicit Widget(int main i) { 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 w(i))); // Use extra parentheses
  Gadget gg2{Widget(wi)}; // Use direct initialization
  std::cout << i << std::endl;
  return 0;
}

Running this program produces the expected output:

...

.

Code Block
languagecpp
Widget constructed
Gadget constructed
Widget constructed 
Gadget constructed
3

Risk Assessment

Not guarding implicit constructor parsing could Syntactically ambiguous declarations can lead to unexpected behaviorprogram execution. However, it is likely that rudimentary testing would uncover violations of this rule.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

OOP31

DCL53-CPP

low

Low

likely

Unlikely

low

Medium

P9

P2

L2

L3

Automated Detection

Tool

Version

Checker

Description

 PRQA QA-C++

  Include PagePRQA QA-C++_vPRQA QA-C++_v

2510

 

 

Bibliography

...

CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

LANG.STRUCT.DECL.FNEST

Nested Function Declaration

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C++1109, C++2510
Klocwork
Include Page
Klocwork_V
Klocwork_V
CERT.DCL.AMBIGUOUS_DECL
LDRA tool suite
Include Page
LDRA_V
LDRA_V

296 S

Partially implemented

Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_CPP-DCL53-a
CERT_CPP-DCL53-b
CERT_CPP-DCL53-c

Parameter names in function declarations should not be enclosed in parentheses
Local variable names in variable declarations should not be enclosed in parentheses
Avoid function declarations that are syntactically ambiguous

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C++: DCL53-CPP

Checks for declarations that can be confused between:

  • Function and object declaration
  • Unnamed object or function parameter declaration

Rule fully covered.

Clang
Include Page
Clang_V
Clang_V
-Wvexing-parse
SonarQube C/C++ Plugin
Include Page
SonarQube C/C++ Plugin_V
SonarQube C/C++ Plugin_V
S3468

Related Vulnerabilities

Search for other vulnerabilities resulting from the violation of this rule on the CERT website.

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"


...

Image Modified Image Modified Image Modified