Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which constructor does this semantics call?

Tags:

c++

c++11

This is a construct of string:

string s {"Hello world!"};

And string class has the following constructor that I think might be bound to:

From char pointer/array:

string (const char* s);

From initializer_list:

string (initializer_list<char> il);

At first I thought it will be calling initializer_list because the parameters are braced. Then I saw the initializer_list takes an argument of char but that was a string.

Then I realize the {""} can be implicitly converted to char[] thus it might call the first constructor... In this case, is a pair of parentheses automatically added around the list(e.g. like ({""}))?

So which constructor does it actually call?


How about this:

double d1 (100);
double d2 {100};

Are they the same?

Assume string also has a constructor like string(initializer_list<string>), then which one will it call?

like image 328
SwiftMango Avatar asked Dec 12 '22 12:12

SwiftMango


1 Answers

In C++11, curly braces {} are used for uniform initialization. Any object that can be initialized can be initialized with curly braces. Preference is given to constructors that take initializer_list<T>, so if a curly-brace initialization could match more than one constructor and one of them takes an initializer_list, that will be selected. However, if no initializer_list constructors match, the other constructors are also considered.

For example:

vector<int> two_elems{5,10}; // A vector containing two elements
vector<int> five_elems(5,10); // A vector containing five elements
vector<int> five_elems_also{10,10,10,10,10}; // Equivalent to the above

In your example, you're initializing a string with a char const[13]. This would match initializer_list<char const*>, if string had such a constructor, but it doesn't match initializer_list<char>. So the parameter is instead matched against the other constructors, and the constructor taking char const* is the best match.

Note that because of the ambiguity, it's probably best not to initialize containers (such as vector, set, list, or even string) using brace initialization unless you intend to use the initializer_list constructors. For example, there's no way to call vector<size_t>'s constructor that takes size_t, T using brace-initialization, since that argument list will also match the initializer_list constructor. As another example:

vector<char const*> vec_of_strs{ 5, 0 }; // Creates a vector holding *5* nullptrs
vector<unsigned> vec_of_nums{ 5, 0 }; // Creates a vector holding *2* numbers

It's not immediately obvious that the two lines above are calling very different constructors, and if the type of one were changed to the other during maintenance (or the code occurred in a template), programmers may be very surprised at the sudden behavior change.

like image 186
Adam H. Peterson Avatar answered Dec 14 '22 00:12

Adam H. Peterson