Uploaded image for project: 'Rules Repository'
  1. Rules Repository
  2. RSPEC-5964

"std::initializer_list" constructor should not overlap with other constructors

    XMLWordPrintable

    Details

    • Message:
      Modify this class to remove the overlap between the "initializer_list" constructor and the other constructors.
    • Highlighting:
      Hide

      Primary location: the class declaration
      Secondary location: the "initializer_list" constructor(s) and the overlapping constructors

      Show
      Primary location: the class declaration Secondary location: the "initializer_list" constructor(s) and the overlapping constructors
    • Default Severity:
      Minor
    • Impact:
      Low
    • Likelihood:
      Low
    • Default Quality Profiles:
      Sonar way
    • Covered Languages:
      C++
    • Remediation Function:
      Constant/Issue
    • Constant Cost:
      10min
    • Analysis Level:
      Semantic Analysis
    • Analysis Scope:
      Main Sources, Test Sources

      Description

      When a class has a constructor accepting initializer_list of type X and another constructor that has n parameters of either type X or a type that can be converted to X, the constructor call resolution becomes complex. This makes code hard to reason about and might lead to calls resolving to unexpected constructors. What makes it even more complex, is that the constructor resolution rules are different if X is a type template parameter.

      This rule flags classes that have constructors overlapping with the initializer_list constructor. It is recommended to simplify the class by:

      • A technical change: replace initializer_list parameter by a std::vector, a std::array, or a variadic template. This way the caller is forced to be more explicit.
      • A design change: make the construction of an object of type X taking object(s) of type Y as parameters equivalent to constructing it with an initializer list containing the object(s) of type Y. This way you can reduce the number of overlapping constructors to the one that takes initializer_list.

      Noncompliant Code Example

      class A { // Noncompliant
      public:
        A();
        A(int); // This constructor  overlaps with the initializer_list constructor
        A(int, long); // This constructor overlaps with the initializer_list constructor
        A(std::initializer_list<int>); // "initializer_list" constructor
        A(int, A);
        A(int, double);
      };
      
      void f1() {
        A a1(10); // A(int) is called 
        A a2{10}; // The "initializer_list" constructor is called
        A a3(10, 1l); // A(int, long) is called 
        A a4{10, 1l}; // The "initializer_list" constructor is called
        A a5{10, A{}}; // A(int, A) is called
        // A a6{10, 1.2}; // doesn't compile
      }
      
      class B { // Noncompliant
      public:
        B(int); // This constructor overlaps with the initializer_list constructor
        B(int, long); // This constructor doesn't overlap with the initializer_list constructor. See b4
        template<typename T>
        B(std::initializer_list<T>); // "initializer_list" constructor
      };
      
      void f2() {
        B b1(10); // The constructor with single "int" parameter is called 
        B b2{10}; // The "initializer_list" constructor is called
        B b3(10, 1l); // B(int, long) is called
        B b4{10, 1l}; // B(int, long) is called
      }
      

      Compliant Solution

      class A {
      public:
        A();
        A(int);
        A(int, long);
        A(std::vector<int>); 
        A(int, A);
        A(int, double);
      };
      
      void f1() {
        A a1(10); // A(int) is called 
        A a2{10}; // A(int) is called
        A a3(10, 1l); // A(int, long) is called 
        A a4{10, 1l}; // A(int, long)
        A a5{10, A{}}; // A(int, A) is called
        A a6{10, 1.2}; // A(int, A) is called
        A a7 {{1,2,4}}; // vector is called no confusion
      }
      
      class B {
      public:
        B(int, long);
        template<typename T>
        B(std::initializer_list<T>);
      };
      
      void f2() {
        B b1({10}); // The "initializer_list" constructor is called
        B b2{10}; // The "initializer_list" constructor is called
        B b3(10, 1l); // B(int, long) is called
        B b4{10, 1l}; // B(int, long) is called
      }
      

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              Unassigned Unassigned
              Reporter:
              massimo.paladin Massimo PALADIN
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Dates

                Created:
                Updated: