Which constructor is getting called for below string code?

133 Views Asked by At

I was reading c++ primer and came across an example of list initialization for vector where the author mentioned that if list initialization isn't possible, the compiler then looks for other ways to initialize the object. Below is such an example for vector of string.

vector<string> v8{10, "hi"};

This creates a vector of 10 elements with value "hi". This I think is because list initialization falls back on below constructor to create the vector.

vector( size_type count, const T& value, const Allocator& alloc = Allocator());

I tried similar kind of list initialization for String

std::string s1{10, 'c'};

I was expecting it to create a string "cccccccccc" because there is a constructor available for it

basic_string( size_type count, CharT ch, const Allocator& alloc = Allocator() );

But it prints only "c"? Which string constructor is getting used here?

2

There are 2 best solutions below

0
On BEST ANSWER

What's coming into play here are the rules for direct-list-initialization which has one of the syntax as

T object { arg1, arg2, ... };

Here, in the case of std::string s{10, 'c'}, T in the above syntax is std::string and the rule that applies is

Otherwise, the constructors of T are considered, in two phases: All constructors that take std::initializer_list as the only argument, or as the first argument if the remaining arguments have default values, are examined, and matched by overload resolution against a single argument of type std::initializer_list

Since there is a constructor of std::string that takes in an initializer list with the other arguments as default, namely constructor (9)

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

that participates in this resolution since 10 can be implicitly converted to char and what you get is a std::initializer_list<char> of {'\n', 'c'} of 2 characters.


In the case of std::vector<std::string> v{10. "hi"}, T is std::vector<std::string> in the syntax for direct-list-initialization as mentioned above.

Now, since 10 can't be implicitly converted to std::string, no such std::initializer_list<std::string> can be constructed and thus the next alternative for direct-list-initialization is

If the previous stage does not produce a match, all constructors of T participate in overload resolution against the set of arguments that consists of the elements of the braced-init-list, with the restriction that only non-narrowing conversions are allowed.

This produces a match with constructor 3

explicit vector( size_type count,
                 const T& value = T(),
                 const Allocator& alloc = Allocator());

since neither of the conversions is non-narrowing.


Now coming to what you wanted to achieve with std::string s{10, 'c'}, that should be done using std::string s(10, c) where there is no list-initialization involved.

std::string s1{10, 'c'}, s2(10, 'c');
std::cout<<'['<<s1<<']'<<'['<<s2<<']'<<'\n';

Output:

[
c][cccccccccc]

Note that printing s1 produces a new line before the c since it's actually ['\n', 'c'], whereas s2 is exactly what you'd expect i.e. 10 c characters.

0
On

The constructor taking std::initiazer_list gets called.

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

10 is an int, which could convert to char implicitly, and std::initializer_list<char> could be constructed from {10, 'c'} as containing two chars, the 1st one with ASCII value 10, the 2nd one as c.

On the other hand for std::vector<std::string>, {10, "hi"} can't be used to construct std::initializer_list<std::string> since 10 can't be converted to std::string, then the constructor taking std::initializer_list<std::string> won't be used.