Search code examples
c++c++11implicit-conversionstdinitializerlistbraced-init-list

Conversion from initializer_list<const char*> to initializer_list<string> in vector constructor


std::vector‘s initializer list constructor has the form

vector( std::initializer_list<T> init, const Allocator& alloc = Allocator() );

What makes an initialization like std::vector<string> vec{ “foo”, “bar” }; possible? Why does the constructor accept an std::initializer_list<const char*>, even though the std::vectors‘s T is std::string? Where and how does the conversion from std::initializer_list<const char*>to std::initializer_list<string> happen?


Solution

  • I think you should refer to this section of the C++ 17 Standard (11.6.4 List-initialization)

    5 An object of type std::initializer_list is constructed from an initializer list as if the implementation generated and materialized (7.4) a prvalue of type “array of N const E”, where N is the number of elements in the initializer list. Each element of that array is copy-initialized with the corresponding element of the initializer list, and the std::initializer_list object is constructed to refer to that array. [ Note: A constructor or conversion function selected for the copy shall be accessible (Clause 14) in the context of the initializer list. — end note ] If a narrowing conversion is required to initialize any of the elements, the program is ill-formed. [ Example:

    struct X {
    X(std::initializer_list<double> v);
    };
    X x{ 1,2,3 };
    

    The initialization will be implemented in a way roughly equivalent to this:

    const double __a[3] = {double{1}, double{2}, double{3}};
    X x(std::initializer_list<double>(__a, __a+3));
    

    assuming that the implementation can construct an initializer_list object with a pair of pointers. — end example ]

    Pay attention to that there is the conversion constructor for the class std::basic_string

    basic_string(const charT* s, const Allocator& a = Allocator());
    

    So in this declaration

    std::vector<string> vec{ “foo”, “bar” };
    

    at first an object of the type std::initializer_list<std::string> is constructed from the initializer list and then is used as an initializer for the vector of the type std::vector<std::string>.

    If instead you will write

    std::initializer_list<const char *> lst = { "foo", "bar" };
    std::vector<std::string> vec( lst );
    

    then the compiler will issue an error because there is no implicit conversion from the type std::initializer_list<const char *> to the type std::initializer_list<std::string>.

    That is the compiler determines whether it can build std::initializer_list from a braced initializer list to call the initializer-list constructor.