Rule A8-5-4 (advisory, implementation, automated)

If a class has a user-declared constructor that takes a parameter of type std::initializer_list, then it shall be the only constructor apart from special member function constructors.

Rationale

If an object is initialized using {} braced-initialization, the compiler strongly prefers constructor taking parameter of type std::initializer_list to other constructors. Thus, if it is defined in the class, it is initially a sole member of the candidate set of the two-phase overload resolution. Only if no viable std::initializer_list is found, the rest of constructors are considered in the second overload resolution. Such a case can be non-intuitive for developers and can lead to reviewers’ confusion on which constructor was intended to be called. If other constructors (besides the std::initializer_list one and special member functions) are declared in a class, then it is suggested to use, e.g. the std::vector( {1,1} ) syntax instead of std::vector v{1, 1}, which makes the intent clear.

Example

// $Id: A8-5-4.cpp 319328 2018-05-15 10:30:25Z michal.szczepankiewicz $ #include <cstdint> #include <initializer_list> #include <vector> #include <iostream> //non-compliant, there are other constructors //apart from initializer_list one defined class A { public: A() = default; A(std::size_t num1, std::size_t num2) : x{num1}, y{num2} {} A(std::initializer_list<std::size_t> list) : x{list.size()}, y{list.size()} { } private: std::size_t x; std::size_t y; }; class B { public: B() = default; B(std::initializer_list<std::size_t> list) : collection{list} { } private: std::vector<std::size_t> collection; }; void F1() noexcept { A a1{}; A a2{{}}; A a3{0, 1}; recommended A a4({0, 1});// A a5(0, 1); // by exception } void F2() noexcept { B b1{}; B b2{{}}; B b3{1, 2}; recommended B b4({1, 2}); recommended } // Calls A::A() // Calls A::A(std::initializer_list<std::size_t>) // Calls A::A(std::initializer_list<std::size_t>), not Calls A::A(std::initializer_list<std::size_t>), recommended Calls A::A(std::size_t, std::size_t), compliant with A8-5-2 // Calls B::B() // Calls B::B(std::initializer_list<std::size_t>) // Calls B::B(std::initializer_list<std::size_t>), not // Calls B::B(std::initializer_list<std::size_t>),

See also

Effective Modern C++ [13]: Item 7. Distinguish between () and {} when creating objects. ISO/IEC 14882:2014 [3]: 13.3.1.7: [over.match.list]