Sunteți pe pagina 1din 5

10/23/2019 c++ - When to use the brace-enclosed initializer?

- Stack Overflow

Episode #125 of the Stack Overflow podcast is here. We talk Tilde Club and mechanical keyboards. Listen
now

When to use the brace-enclosed initializer?


Asked 7 years, 6 months ago Active 7 years, 6 months ago Viewed 27k times

In C++11, we have that new syntax for initializing classes which gives us a big number of
possibilities how to initialize variables.
90
{ // Example 1
int b(1);
int a{1};
int c = 1;
int d = {1};
43 }
{ // Example 2
std::complex<double> b(3,4);
std::complex<double> a{3,4};
std::complex<double> c = {3,4};
auto d = std::complex<double>(3,4);
auto e = std::complex<double>{3,4};
}
{ // Example 3
std::string a(3,'x');
std::string b{3,'x'}; // oops
}
{ // Example 4
std::function<int(int,int)> a(std::plus<int>());
std::function<int(int,int)> b{std::plus<int>()};
}
{ // Example 5
std::unique_ptr<int> a(new int(5));
std::unique_ptr<int> b{new int(5)};
}
{ // Example 6
std::locale::global(std::locale("")); // copied from 22.4.8.3
std::locale::global(std::locale{""});
}
{ // Example 7
std::default_random_engine a {}; // Stroustrup's FAQ
std::default_random_engine b;
}
{ // Example 8
duration<long> a = 5; // Stroustrup's FAQ too
duration<long> b(5);
duration<long> c {5};
}

For each variable I declare, I have to think which initializing syntax I should use and this slows my
coding speed down. I'm sure that wasn't the intention of introducing the curly brackets.

When it comes to template code, changing the syntax can lead to different meanings, so going the
right way is essential.

I wonder whether there is a universal guideline which syntax one should chose.

https://stackoverflow.com/questions/9976927/when-to-use-the-brace-enclosed-initializer 1/5
10/23/2019 c++ - When to use the brace-enclosed initializer? - Stack Overflow

c++ c++11 initializer-list

edited Apr 2 '12 at 13:03 asked Apr 2 '12 at 12:58


helami
1,004 1 9 14

1 An example of unintended behaviour from {} initialisation: string(50, 'x') vs string{50, 'x'} here – P i Dec 11 '14
at 12:44

3 Answers

I think the following could be a good guideline:

If the (single) value you are initializing with is intended to be the exact value of the object, use
62 copy ( = ) initialization (because then in case of error, you'll never accidentally invoke an
explicit constructor, which generally interprets the provided value differently). In places where
copy initialization is not available, see if brace initialization has the correct semantics, and if so,
use that; otherwise use parenthesis initialization (if that is also not available, you're out of luck
anyway).
If the values you are initializing with are a list of values to be stored in the object (like the
elements of a vector/array, or real/imaginary part of a complex number), use curly braces
initialization if available.
If the values you are initializing with are not values to be stored, but describe the intended
value/state of the object, use parentheses. Examples are the size argument of a vector or the
file name argument of an fstream .

answered Apr 2 '12 at 13:38


celtschk
16.6k 2 30 58

4 @user1304032: A locale is not a string, therefore you wouldn't use copy initialization. A locale also does not
contain a string (it might store that string as implementation detail, but that's not its purpose), therefore you
wouldn't use brace initialization. Therefore the guideline says to use parenthesis initialization. – celtschk Apr 2
'12 at 13:53

2 I personally liked this guideline at most and it also works nice on generic code. There are some exceptions
( T {} or syntactic reasons like the most vexing parse), but in general I think this does a good advice. Note
that this is my subjective opinion, so one should have a look at the other answers too. – helami Apr 2 '12 at
16:22

2 @celtschk : That won't work for non-copyable, non-movable types; type var{}; does. – ildjarn Apr 2 '12 at
18:53

2 @celtschk : I'm not saying it is something that would occur frequently, but it's less typing and works in more
contexts, so what's the downside? – ildjarn Apr 3 '12 at 19:18

2 My guidelines certainly never call for copy-initialization. ;-] – ildjarn Apr 3 '12 at 19:30

https://stackoverflow.com/questions/9976927/when-to-use-the-brace-enclosed-initializer 2/5
10/23/2019 c++ - When to use the brace-enclosed initializer? - Stack Overflow

I am pretty sure there will never be a universal guideline. My approach is to use always curly braces
remembering that
25 1. Initializer list constructors take precedence over other constructors
2. All standard library containers and std::basic_string have initializer list constructors.
3. Curly brace initialization does not allow narrowing conversions.

So round and curly braces are not interchangeable. But knowing where they differ allows me to use
curly over round bracket initialization in most cases (some of the cases where I can't are currently
compiler bugs).

answered Apr 2 '12 at 13:20


juanchopanza
195k 23 319 427

6 Curly braces have the disadvantage that I can call the list constructor by mistake. Round brackets don't. Isn't
that a reason to use the round brackets by default? – helami Apr 2 '12 at 13:36

4 @user: On the int i = 0; I don't think anyone would use int i{0} there, and it might be confusing
(also, 0 is if type int , so there would be no narrowing). For everything else, I would follow Juancho's
advice: prefer {}, beware of the few cases where you should not. Note that there are not that many types that
will take initializer lists as constructor arguments, you can expect containers and container-like types (tuple...)
to have them, but most code will call the appropriate constructor. – David Rodríguez - dribeas Apr 2 '12 at
13:43

3 @user1304032 it depends if you care about narrowing. I do, so I prefer the compiler to tell me that int
i{some floating point} is an error, rather than to silently truncate. – juanchopanza Apr 2 '12 at 13:43

3 Concerning "prefer {}, beware of the few cases where you should not": Say two classes have a semantically
equivalent constructor but one class has a initializer list too. Should the two equivalent constructors be called
differently? – helami Apr 2 '12 at 14:10

3 @helami: "Say two classes have a semantically equivalent constructor but one class has a initializer list too.
Should the two equivalent constructors be called differently?" Say I run into the most-vexing parse; that can
happen on any constructor for any instance. It's much easier to avoid this if you just use {} to mean
"initialize" unless you absolutely cannot. – Nicol Bolas Apr 2 '12 at 19:55

Outside of generic code (i.e. templates), you can (and I do) use braces everywhere. One
advantage is that it works everywhere, for instance even for in-class initialization:
16
struct foo {
// Ok
std::string a = { "foo" };

// Also ok
std::string b { "bar" };

// Not possible
std::string c("qux");

// For completeness this is possible


std::string d = "baz";
};

https://stackoverflow.com/questions/9976927/when-to-use-the-brace-enclosed-initializer 3/5
10/23/2019 c++ - When to use the brace-enclosed initializer? - Stack Overflow

or for function arguments:

void foo(std::pair<int, double*>);


foo({ 42, nullptr });
// Not possible with parentheses without spelling out the type:
foo(std::pair<int, double*>(42, nullptr));

For variables I don't pay much attention between the T t = { init }; or T t { init }; styles, I
find the difference to be minor and will at worst only result in a helpful compiler message about
misusing an explicit constructor.

For types that accept std::initializer_list though obviously sometimes the non-
std::initializer_list constructors are needed (the classical example being std::vector<int>
twenty_answers(20, 42); ). It's fine to not use braces then.

When it comes to generic code (i.e. in templates) that very last paragraph should have raised some
warnings. Consider the following:

template<typename T, typename... Args>


std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T { std::forward<Args>(args)... } }; }

Then auto p = make_unique<std::vector<T>>(20, T {}); creates a vector of size 2 if T is e.g.


int , or a vector of size 20 if T is std::string . A very telltale sign that there is something very
wrong going on here is that there's no trait that can save you here (e.g. with SFINAE):
std::is_constructible is in terms of direct-initialization, whereas we're using brace-initialization
which defers to direct-initialization if and only if there's no constructor taking
std::initializer_list interfering. Similarly std::is_convertible is of no help.

I've investigated if it is in fact possible to hand-roll a trait that can fix that but I'm not overly optimistic
about that. In any case I don't think we would be missing much, I think that the fact that
make_unique<T>(foo, bar) result in a construction equivalent to T(foo, bar) is very much
intuitive; especially given that make_unique<T>({ foo, bar }) is quite dissimilar and only makes
sense if foo and bar have the same type.

Hence for generic code I only use braces for value initialization (e.g. T t {}; or T t = {}; ),
which is very convenient and I think superior to the C++03 way T t = T(); . Otherwise it's either
direct initialization syntax (i.e. T t(a0, a1, a2); ), or sometimes default construction ( T t;
stream >> t; being the only case where I use that I think).

That doesn't mean that all braces are bad though, consider the previous example with fixes:

template<typename T, typename... Args>


std::unique_ptr<T> make_unique(Args&&... args)
{ return std::unique_ptr<T> { new T(std::forward<Args>(args)...) }; }

This still uses braces for constructing the std::unique_ptr<T> , even though the actual type depend
on template parameter T .

answered Apr 2 '12 at 15:30

https://stackoverflow.com/questions/9976927/when-to-use-the-brace-enclosed-initializer 4/5
10/23/2019 c++ - When to use the brace-enclosed initializer? - Stack Overflow

Luc Danton
31.4k 5 59 106

@interjay Some of my examples may indeed need to use unsigned types instead, e.g. make_unique<T>
(20u, T {}) for T being either unsigned or std::string . Not too sure on the details. (Note that I also
commented on expectations regarding direct initialization vs brace initialization regarding e.g. perfect-
forwarding functions.) std::string c("qux"); hasn't been specified to work as an in-class initialization to
avoid ambiguities with member function declarations in the grammar. – Luc Danton Apr 2 '12 at 17:20

@interjay I don't agree with you on the first point, feel free to check 8.5.4 List initialization and 13.3.1.7
Initialization by list-initialization. As for the second, you need to take a closer look at what I wrote (which is
regarding in-class initialization) and/or the C++ grammar (e.g. member-declarator, which references brace-or-
equal-initializer). – Luc Danton Apr 2 '12 at 17:38

Hmm, you're right - I was testing with GCC 4.5 earlier which seemed to confirm what I was saying, but GCC
4.6 does agree with you. And I did miss the fact that you were talking about in-class initialization. My
apologies. – interjay Apr 2 '12 at 18:09

https://stackoverflow.com/questions/9976927/when-to-use-the-brace-enclosed-initializer 5/5

S-ar putea să vă placă și