Hello,
I would like to report what I will call "constructor ambiguity" for classes which have multiple constructors, one of which have takes an initializer list.
The ambiguity is for us humans, not the compiler, and is due to the two things:
- The same syntax is used for uniform initialization and initializer lists.
- Any constructor taking an initializer list is preferred when braces are used.
For example:
std::string sa(32, 'A'); // Creates a string of 32 characters i.e. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
std::string sb{32, 'A'}; // Creates a string of 2 characters i.e. " A"
std::vector<int> va(10, -1); // Creates a vector of 10 elements, each one initialized to -1
std::vector<int> vb{10, -1}; // Creates a vector of 2 elements (10 and -1)
The code looks very similar and yet the results are very different. This is unfortunate because uniform initialization is preferred especially as it can avoid bugs due to unintential casts and truncation/narrowing of values.
I find this ambiguity unnecessary as if the intent is to provide an initializer list, then this can be done explicitly:
std::vector<int> vb1({10, -1}); // Creates a vector of 2 elements (10 and -1) std::vector<int> vb2{{10, -1}}; // Creates a vector of 2 elements (10 and -1)
This preference to use a constructor taking an initializer list can easily break code if such a constructor is added at a later date. For example:
struct A { explicit A(int);
};
A a{2}; // Create an object a of type A
Then later on an initializer list constructor is added:
struct A { explicit A(int);
A(const std::initializer_list<int>&)
};
Now the call to create object 'a' will use the constructor taking an initializer list which, of course, can do something completely different. It could be extremely difficult to find why there was such a change in behaviour.
I apologize but I do not know if this has already been mentioned or addressed. If not, what do you think of this? Is this considered to be one of the many idioms of C++?
Gawain Bolton