C++ - Uniform initializer with std::string
Asked Answered
R

2

13

I am trying the uniform intializer with the string class of C++. Below is the code:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string str1 {"aaaaa"};
    string str2 {5, 'a'};
    string str3 (5, 'a');

    cout << "str1: " << str1 << endl;
    cout << "str2: " << str2 << endl;
    cout << "str3: " << str3 << endl;

    return 0;
}

The output would be:

str1: aaaaa
str2: a
str3: aaaaa

This made me scratched my head. Why str2 cannot achieved the desired result as str3?

Reiko answered 6/8, 2016 at 22:25 Comment(1)
"Uniform" initialization...Tamalatamale
K
10

std::string has a constructor that takes an initializer_list argument.

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

That constructor always gets precedence when you use a braced-init-list to construct std::string. The other constructors are only considered if the elements in the braced-init-list are not convertible to the type of elements in the initializer_list. This is mentioned in [over.match.list]/1.

Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.

In your example, the first argument 5 is implicitly convertible to char, so the initializer_list constructor is viable, and it gets chosen.

This is evident if you print each character in the strings as ints

void print(char const *prefix, string& s)
{
    cout << prefix << s << ", size " << s.size() << ": ";
    for(int c : s) cout << c << ' ';
    cout << '\n';
}

string str1 {"aaaaa"};
string str2 {5, 'a'};
string str3 (5, 'a');

print("str1: ", str1);
print("str2: ", str2);
print("str3: ", str3);

Output:

str1: aaaaa, size 5: 97 97 97 97 97 
str2: a, size 2: 5 97 
str3: aaaaa, size 5: 97 97 97 97 97 

Live demo

Keldah answered 6/8, 2016 at 22:48 Comment(2)
Thanks. Kind of got the idea what is going on here. I always prefer the uniform initialization since I was told it is the best practise from now on (I am still new to C++). Looks like I got to be careful from now on.Reiko
@Hilman As you've found out, it doesn't always work so uniformly. With vector<int> for instance, vector<int> v1{4,2}, v2(4,2);. Here v1 contains 2 integers, 4 and 2, while v2 contains 4 integers, each initialized to 2.Keldah
Z
10

You are using std::string's constructor with this siganture

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

And the compiler treated 5 as a char type, which translates to an ASCII type that isn't printed on screen. If you change that 5 to a printable value, say A whose ASCII value is 65,

string str2 {65, 'a'};

it will print:

Aa

See it Live on Coliru with an additional illustration

Zhukov answered 6/8, 2016 at 22:37 Comment(0)
K
10

std::string has a constructor that takes an initializer_list argument.

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

That constructor always gets precedence when you use a braced-init-list to construct std::string. The other constructors are only considered if the elements in the braced-init-list are not convertible to the type of elements in the initializer_list. This is mentioned in [over.match.list]/1.

Initially, the candidate functions are the initializer-list constructors ([dcl.init.list]) of the class T and the argument list consists of the initializer list as a single argument.

In your example, the first argument 5 is implicitly convertible to char, so the initializer_list constructor is viable, and it gets chosen.

This is evident if you print each character in the strings as ints

void print(char const *prefix, string& s)
{
    cout << prefix << s << ", size " << s.size() << ": ";
    for(int c : s) cout << c << ' ';
    cout << '\n';
}

string str1 {"aaaaa"};
string str2 {5, 'a'};
string str3 (5, 'a');

print("str1: ", str1);
print("str2: ", str2);
print("str3: ", str3);

Output:

str1: aaaaa, size 5: 97 97 97 97 97 
str2: a, size 2: 5 97 
str3: aaaaa, size 5: 97 97 97 97 97 

Live demo

Keldah answered 6/8, 2016 at 22:48 Comment(2)
Thanks. Kind of got the idea what is going on here. I always prefer the uniform initialization since I was told it is the best practise from now on (I am still new to C++). Looks like I got to be careful from now on.Reiko
@Hilman As you've found out, it doesn't always work so uniformly. With vector<int> for instance, vector<int> v1{4,2}, v2(4,2);. Here v1 contains 2 integers, 4 and 2, while v2 contains 4 integers, each initialized to 2.Keldah

© 2022 - 2024 — McMap. All rights reserved.