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
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]