16

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};
deft_code
  • 57,255
  • 29
  • 141
  • 224
smancill
  • 744
  • 1
  • 6
  • 13
  • 3
    First, you'll need to tell us wich version of wich compiler you're using, because it's certainly a bug or feature not implemented in your current setup. – Klaim Sep 30 '11 at 15:05
  • Here is an ideone snipped that demonstrates the first error, [ideone.com/zXIto](http://ideone.com/zXIto). – deft_code Sep 30 '11 at 16:27

3 Answers3

21

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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
11

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.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • @deft_code nope, because `5.0` is a `double`. Narrowing conversions are defined by the types, not the values. I think I'll change my examples to `5.0` to avoid giving the idea that only truncation counts :) – R. Martinho Fernandes Sep 30 '11 at 16:37
  • @deft_code well, I meant, *this* kind of narrowing conversion (floating-point to integral) is defined by the types. Some narrowing conversions depend on the values if they are constant expressions. – R. Martinho Fernandes Sep 30 '11 at 16:54
  • It has been fixed in 4.6, isn't there a workaround for versions before 4.6? – Mansuro Sep 30 '11 at 20:09
4

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.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I am sorry, I forgot the semicolon writing the question. I will put the error now. – smancill Sep 30 '11 at 15:01
  • OK. It works in GCC 4.6.1, but not in 4.4.3. The thing about the new standard is that it isn't thoroughly supported yet. :-( – Kerrek SB Sep 30 '11 at 17:15