CERT.DCL.AMBIGUOUS_DECL

Do not write syntactically ambiguous declarations.

It is possible to devise syntax that can ambiguously be interpreted as either an expression statement or a declaration.

Vulnerability and risk

Syntax of this sort is called a vexing parse because the compiler must use disambiguation rules to determine the semantic results.

Mitigation and prevention

Do not write syntactically ambiguous declarations.

Example 1

Copy
  #include <mutex>
  #include <iostream>
  
  static std::mutex m;
  static int shared_resource;
  
  struct Widget1 {
    Widget1() { std::cout << "Constructed" << std::endl; }
  };
 
 struct Widget2 {
   explicit Widget2(int i) { std::cout << "Widget2 constructed" << std::endl; }
 };
 
 struct Gadget {
   explicit Gadget(Widget2 wid) { std::cout << "Gadget constructed" << std::endl; }
 };
 
 void increment_by_42() {
   std::unique_lock<std::mutex>(m);    //Noncompliant Code
   shared_resource += 42;
 }
 
 void f1() {
   Widget1 w1();                       //Noncompliant Code
 }
 
 void f2() {
   int i = 3;
   Gadget g(Widget2(i));               //Noncompliant Code
   std::cout << i << std::endl;
 }

The declaration in line 20 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.

The declaration in line 25 is syntactically ambiguous where the code could be either 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.

In line 30 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.

Example 2

Copy
  #include <mutex>        
  #include <iostream>
  static std::mutex m;
  static int shared_resource;
  
  struct Widget1 {
    Widget1() { std::cout << "Constructed" << std::endl; }           
  };
  struct Widget2 {
   explicit Widget2(int i) { std::cout << "Widget constructed" << std::endl; }
 };
 
 struct Gadget {
   explicit Gadget(Widget2 wid) { std::cout << "Gadget constructed" << std::endl; }
 };
 
 void increment_by_42() {
   std::unique_lock<std::mutex> lock(m);
   shared_resource += 42;
 }
 
 void f1() {
   Widget1 w1; // Elide the parentheses
   Widget1 w2{}; // Use direct initialization
 }
 
 void f2() {
   int i = 3;
   Gadget g1((Widget2(i))); // Use extra parentheses
   Gadget g2{Widget2(i)}; // Use direct initialization
   std::cout << i << std::endl;
 }

This example explains how to fix the noncompliant codes in Example 1.