Search code examples
c++c++11constructorinitializer-liststd

are there any plans in C++ standard to address inconsistency of initializer list constructors?


initializer list constructors in C++ often cause trouble; for example

using std::vector;
using std::string;
vector<string> v{3}; // vector of three empty strings
vector<int> u{3}; // vector of one element with value 3

(Just to clarify, I mean <int> constructor is an initializer list constructor, while the <string> one is not.)

The int case matches the initializer list constructor, while the string case doesn't. This is somewhat ugly, and often causes trouble. It was also noted in an early chapter (item 7) in Scott Meyers' Effective Modern C++, and he describes it as a somewhat unpleasant part of the standard, that whenever an initializer list constructor is available, compilers will jump through hoops to try to match it, giving it priority over every single other constructor.

Of course, the int case can be easily fixed by changing u{3} to u(3), but that's not the point.

Is this desirable behavior? Has there been any discussion or plans by the C++ standard committee to fix this type of ambiguity / unpleasantness? One example would be requiring initializer list constructors to be called like this: vector<int> u({3}), which is already currently legal.


Solution

  • Has there been any discussion or plans by the C++ standard committee to fix this type of ambiguity / unpleasantness?

    There have been many fixes to initialization since C++11. For instance, you initially couldn't copy construct aggregates using list-initialization (CWG 1467). This really minor fix broke some code in an undesirable way that lead to a new issue to refine that previous fix to undo it if there's an initializer_list constructor (CWG 2137). It's hard to touch anything in these clauses without lots of unexpected consequences and breaking code, even in small cases. I doubt there will be a push to make any kind of large change to initialization in the future. At this point, the amount of code breakage would be tremendous.

    The best solution is just to be aware about the pitfalls of initialization and be careful about what you're doing. My rule of thumb is to only use {}s when I deliberately need the behavior that {} provides, and () otherwise.

    Note that this isn't really any different from the more well-known pitfall of:

    vector<int> a{10}; // vector of 1 element
    vector<int> b(10); // vector of 10 elements
    

    One example would be requiring initializer list constructors to be called like this: vector<int> u({3}), which is already currently legal.

    You have the same problem you had before, for the same reasons:

    vector<int> u({3});    // vector of one element: 3
    vector<string> v({3}); // vector of three elements: "", "", and ""
    

    Even if you were to require the former (which would be impossible), you couldn't make the latter ill-formed.