Question

I cannot understand when and how to use the new uniform initialization syntax in C++11.
For example, I get this:

std::string a{"hello world"}; // OK
std::string b{a};  // NOT OK

Why does it not work in the second case? The error is:

error: no matching function for call to ‘std::basic_string<char>::basic_string(<brace enclosed initializer list>)’    

with this version of g++ g++ (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2.

And with primitive data, what syntax should I use?

int i = 5;
int i{5};
int i = {5};
Was it helpful?

Solution

The compile error for

// T t{u};
std::string a;
std::string b{a};

Is a combination of four things

  • The draft until not long ago said that if T has an initializer list constructor (std::string has one, taking char elements), that the initializer list is passed itself as an argument. So the argument to the constructor(s) is not a, but {a}.

  • The draft says that an initializer list initializing a reference is done not by direct binding, but by first constructing a temporary out of the element in the initializer list, and then binding the target reference to that temporary.

  • The draft says that when initializing a reference by an initializer list, when the initialization of the reference is not direct binding, that the conversion sequence is a user defined conversion sequence.

  • The draft says that when passing the initializer list itself when considering constructors of class X as candidates in an overload resolution scenario in a context like the above, then when considering a first constructor parameter of type "reference to cv X" (cv = const / volatile) - in other words highly likely a copy or move constructor, then no user defined conversions are allowed. Otherwise, if such a conversion would be allowed, you could always run in ambiguities, because with list initialization you are not limited to only one nested user defined conversion.

The combination of all the above is that no constructor can be used to take the {a}. The one using initializer_list<char> does not match, and the others using string&& and const string& are forbidden, because they would necessitate user defined conversions for binding their parameter to the {a}.

Note that more recent draft changed the first rule: They say that if no initializer list constructor can take the initializer list, that then the argument list consists of all the elements of the initializer list. With that updated rule, your example code would work fine.

OTHER TIPS

The first example is supposed to work, calling the copy constructor. If you're using GCC, this has been fixed in 4.6.

The last examples have a slight non stylistic difference.

int i = 5.0;   // fine, stores 5
int i{5.0};    // won't compile!
int i = {5.0}; // won't compile!

The difference is that uniform initialization syntax does not allow narrowing conversions. You may want to take this into consideration when choosing between them.

It doesn't work because you forgot the semicolon:

std::string b{a};
               ^^^

Otherwise this syntax is fine, and it calls the copy constructor.

For the second question, use int i{5}; if you want to be uniform, though int i = 5; is probably more readable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top