How to use C++11 uniform initialization syntax?
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, takingchar
elements), that the initializer list is passed itself as an argument. So the argument to the constructor(s) is nota
, 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.
Usage of Uniform Initialization Syntax
In case of simple types, like int in your case, there is no difference. However, initialization of std::vector from STL will be completely different
std::vector<int> v1(3,1); // v1 consists of: 1, 1, 1
std::vector<int> v2{3,1}; // v2 consists of: 3, 1
Have a look at this answer if you want to see why generally brace {} initialization is better, however quoting from Scott Meyer's book Effective Modern C++, which I highly recommend:
[...] So why isn’t this Item
entitled something like “Prefer braced initialization syntax”?
The drawback to braced initialization is the sometimes-surprising behavior that
accompanies it. [...]
Preferred way of initialization in c++11
For something simple, such as the int
in your example, I'd agree that
int i=0;
is probably the most commonly understood (among programmers), but there are advantages to using the brace-initialization that, to me, make it preferable. For instance
int i = 3.99; // i gets 3; no warning, no error
int i{3.99}; // i gets 3; warning: "narrowing conversion"
It helps to write more bug-free code and is therefore a better way to do it in my view.
Mixing it with auto
is more perilous. I typically use auto
only for:
- the temporary variable in a range-for loop (e.g.
for (const auto &n : mycollection)
) - to simplify declaration of a named lambda
- for iterator instances when I use them explicitly (rather than range-for)
- templated code where doing so avoids creating a lengthy typedef
C++11 uniform initialization: ambiguity between initializer list and multiple-parameter constructors?
it seems like the compiler interprets Foo b {1, 2} as a list
initialization, and calls constructor 2. Is the () syntax the only way
to force the compiler to consider other kinds of constructors when an
initializer-list constructor is present?
Quotes from standard draft explains this well:
9.4.5.2 [dcl.init.list] (emphasis mine):
A constructor is an initializer-list constructor if its first
parameter is of type std::initializer_list or reference to cv
std::initializer_list for some type E, and either there are no
other parameters or else all other parameters have default arguments
([dcl.fct.default]).
[Note 2: Initializer-list constructors are
favored over other constructors in list-initialization
([over.match.list]). Passing an initializer list as the argument to
the constructor template template C(T) of a class C does not
create an initializer-list constructor, because an initializer list
argument causes the corresponding parameter to be a non-deduced
context ([temp.deduct.call]). — end note]
and 12.4.2.8 [over.match.list]:
When objects of non-aggregate class type T are list-initialized such
that [dcl.init.list] specifies that overload resolution is performed
according to the rules in this subclause or when forming a
list-initialization sequence according to [over.ics.list], overload
resolution selects the constructor in two phases:
If the initializer list is not empty or T has no default constructor,
overload resolution is first performed where 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.Otherwise, or if no viable initializer-list constructor is found,
overload resolution is performed again, where the candidate functions
are all the constructors of the class T and the argument list consists
of the elements of the initializer list.
C++11 Uniform initialization for constructor and operator=
operator=
is used for assignment, not initialisation. To provoke that, you'll have to assign to an already existing object.
Table t = {blah, blah}; // initialisation
t = {wibble, wobble}; // assignment
Uniform initialization syntax or type conversion?
First note that what you are doing there is not initialization, is a type conversion followed by an assignment. I strongly recommend C++ casting operators (static_cast
in this case) over C casts and these constructor-based castings.
That said, the main difference between uniform initialization and the other is that uniform initialization doesn't allow (See the note) narrowing conversions such these you are doing, float to int. This is helpful when writting constants or initializing variables, since initializing an int
with 3.141592654
has no sense at all because the fractional part will be stripped out.
NOTE: I remember the initial proposal for uniform-initialization explicitly stating that it disallows narrowing conversions, so if I had understood it correctly, code like yours should not compile.
I have tested it and seems like compilers emmit warnings about the narrowing conversions instead of aborting compilation. Indeed, that warnings are useful too, and you could allways use a -Werror
flag.
Uniform Initialization to call a constructor other than initializer list
This code:
MyClass a2{ (1, 2) };
could not call the 2 argument constructor. What is happening is that in the expression (1,2)
, the 1
is evaluated before the comma operator, it is discarded, and the result of the expression is 2
. This obviously calls the initializer_list
constructor.
Enable warnings, e.g. with -Wall
and the compiler will tell you about this.
Note that the same thing happens in the vector<int> a{ (1, 2) };
call. The 1
is discarded, and the constructor of vector that takes a single argument is invoked.
Uniform initialization on member initializer list error
This seems like a MSVC bug. The difference is that the struct
version is an aggregate, and the class
version is not (on account of the default private access specifier).
The class version is value initialized by {}
. The struct version is aggregate initialized. A conforming compiler should just list initialize condition_
with {}
, because you didn't provide an initializer for it.
But MSVC seems to be stumbling on the fact that members of an aggregate are copy initialized from the corresponding initializer in the initializer list. It seems to check for the copy c'tor, even if it isn't supposed to actually use it.
This is further supported by the fact it knows what to do when an object of the same type is initialized outside of a member initializer list.
Related Topics
Fast Cross-Platform C/C++ Image Processing Libraries
Narrowing Conversions in C++0X. Is It Just Me, or Does This Sound Like a Breaking Change
Are There Practical Uses for Dynamic-Casting to Void Pointer
Setupdigetdeviceproperty Usage Example
Set Precision of Std::To_String When Converting Floating Point Values
Getline Not Working Properly? What Could Be the Reasons
Why Is "Using Namespace X;" Not Allowed at Class/Struct Level
What Is the Default Value for C++ Class Members
Compiling a Static Executable with Cmake
Compile Time Sizeof_Array Without Using a MACro
Difference Between <String> and <String.H>
How Is C++ Std::Vector Implemented
Std::Strings's Capacity(), Reserve() & Resize() Functions
How to Avoid Qt App.Exec() Blocking Main Thread
C++ Visual Studio Character Encoding Issues