Search code examples
c++stringc++11constructorinitializer-list

Why does this code to initialize a string to a single character call the initializer_list constructor?


I was recently working on a C++ project and came across an edge case with the string constructors that I can't fully understand. The relevant code (which you can run here) is as follows:

#include <iostream>
#include <string>
using namespace std;

int main() {
    string directParens(1, '*');
    string directBraces{1, '*'};
    string indirectBraces = {1, '*'};

    cout << directParens.size() << endl;   // 1
    cout << directBraces.size() << endl;   // 2
    cout << indirectBraces.size() << endl; // 2
    return 0;
}

The brace-initialized versions of the strings end up having two characters in them, namely, a char with numeric value 1 followed by a star.

I don't understand why the brace-initialized versions of the string invoke the initializer_list constructor rather than the constructor taking in a size and a character. The initializer_list constructor has this signature:

basic_string(std::initializer_list<CharT> init, 
             const Allocator& alloc = Allocator());

Given that string is an alias for basic_string char, the specific signature would be

string(std::initializer_list<char> init, 
       const Allocator& alloc = Allocator());

How is the initializer {1, '*'}, which contains elements both of type int and of type char, matching this constructor? I was under the impression that all literals in an std::initializer_list must have the same type - is that incorrect?


Solution

  • How is the initializer {1, '*'}, which contains elements both of type int and of type char, matching this constructor?

    Because both the literal 1 and the character '*' are convertible to char without using a narrowing conversion. Therefore, they are eligible to call the initializer_list<char> constructor. And initializer_list constructors always have priority when using list initialization. If it is possible to call the initializer_list constructor from the parameters, then it will.

    Never use braced-init-lists with containers unless you intend the elements in the list to be elements of the container. If you intend them to be constructor parameters, then use a constructor.