Confused about Initializing Variables/Class Members [duplicate] - c++

MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?

Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":
List initialization does not allow narrowing (§iso.8.5.4). That is:
An integer cannot be converted to another integer that cannot hold its value. For example, char
to int is allowed, but not int to char.
A floating-point value cannot be converted to another floating-point type that cannot hold its
value. For example, float to double is allowed, but not double to float.
A floating-point value cannot be converted to an integer type.
An integer value cannot be converted to a floating-point type.
Example:
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
The only situation where = is preferred over {} is when using auto keyword to get the type determined by the initializer.
Example:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Conclusion
Prefer {} initialization over alternatives unless you have a strong reason not to.

There are already great answers about the advantages of using list initialization, however my personal rule of thumb is NOT to use curly braces whenever possible, but instead make it dependent on the conceptual meaning:
If the object I'm creating conceptually holds the values I'm passing in the constructor (e.g. containers, POD structs, atomics, smart pointers etc.), then I'm using the braces.
If the constructor resembles a normal function call (it performs some more or less complex operations that are parametrized by the arguments) then I'm using the normal function call syntax.
For default initialization I always use curly braces.
For one, that way I'm always sure that the object gets initialized irrespective of whether it e.g. is a "real" class with a default constructor that would get called anyway or a builtin / POD type. Second it is - in most cases - consistent with the first rule, as a default initialized object often represents an "empty" object.
In my experience, this ruleset can be applied much more consistently than using curly braces by default, but having to explicitly remember all the exceptions when they can't be used or have a different meaning than the "normal" function-call syntax with parenthesis (calls a different overload).
It e.g. fits nicely with standard library-types like std::vector:
vector<int> a{10, 20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10, 20); //Parentheses -> uses arguments to parametrize some functionality,
vector<int> c(it1, it2); //like filling the vector with 10 integers or copying a range.
vector<int> d{}; //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements

There are MANY reasons to use brace initialization, but you should be aware that the initializer_list<> constructor is preferred to the other constructors, the exception being the default-constructor. This leads to problems with constructors and templates where the type T constructor can be either an initializer list or a plain old ctor.
struct Foo {
Foo() {}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}
Assuming you don't encounter such classes there is little reason not to use the intializer list.

Update (2022-02-11):
Note that there are more recent opinions on that subject to the one originally posted (below), which argue against the preference of the {} initializer, such as Arthur Dwyer in his blog post on The Knightmare of Initialization in C++.
Original Answer:
Read Herb Sutter's (updated) GotW #1.
This explains in detail the difference between these, and a few more options, along with several gotchas that are relevant for distinguishing the behavior of the different options.
The gist/copied from section 4:
When should you use ( ) vs. { } syntax to initialize objects? Why?
Here’s the simple guideline:
Guideline: Prefer to use initialization with { }, such as vector
v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because
it’s more consistent, more correct, and avoids having to know about
old-style pitfalls at all. In single-argument cases where you prefer
to see only the = sign, such as int i = 42; and auto x = anything;
omitting the braces is fine. …
That covers the vast majority of cases. There is only one main
exception:
… In rare cases, such as vector v(10,20); or auto v =
vector(10,20);, use initialization with ( ) to explicitly call a
constructor that is otherwise hidden by an initializer_list
constructor.
However, the reason this should be generally “rare” is because default
and copy construction are already special and work fine with { }, and
good class design now mostly avoids the resort-to-( ) case for
user-defined constructors because of this final design guideline:
Guideline: When you design a class, avoid providing a constructor that
ambiguously overloads with an initializer_list constructor, so that
users won’t need to use ( ) to reach such a hidden constructor.
Also see the Core Guidelines on that subject: ES.23: Prefer the {}-initializer syntax.

It only safer as long as you don't build with -Wno-narrowing like say Google does in Chromium. If you do, then it is LESS safe. Without that flag the only unsafe cases will be fixed by C++20 though.
Note:
A) Curly brackets are safer because they don't allow narrowing.
B) Curly brackers are less safe because they can bypass private or deleted constructors, and call explicit marked constructors implicitly.
Those two combined means they are safer if what is inside is primitive constants, but less safe if they are objects (though fixed in C++20)

Related

which constructor will be called when operator = is used in class initialization? [duplicate]

MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?
Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":
List initialization does not allow narrowing (§iso.8.5.4). That is:
An integer cannot be converted to another integer that cannot hold its value. For example, char
to int is allowed, but not int to char.
A floating-point value cannot be converted to another floating-point type that cannot hold its
value. For example, float to double is allowed, but not double to float.
A floating-point value cannot be converted to an integer type.
An integer value cannot be converted to a floating-point type.
Example:
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
The only situation where = is preferred over {} is when using auto keyword to get the type determined by the initializer.
Example:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Conclusion
Prefer {} initialization over alternatives unless you have a strong reason not to.
There are already great answers about the advantages of using list initialization, however my personal rule of thumb is NOT to use curly braces whenever possible, but instead make it dependent on the conceptual meaning:
If the object I'm creating conceptually holds the values I'm passing in the constructor (e.g. containers, POD structs, atomics, smart pointers etc.), then I'm using the braces.
If the constructor resembles a normal function call (it performs some more or less complex operations that are parametrized by the arguments) then I'm using the normal function call syntax.
For default initialization I always use curly braces.
For one, that way I'm always sure that the object gets initialized irrespective of whether it e.g. is a "real" class with a default constructor that would get called anyway or a builtin / POD type. Second it is - in most cases - consistent with the first rule, as a default initialized object often represents an "empty" object.
In my experience, this ruleset can be applied much more consistently than using curly braces by default, but having to explicitly remember all the exceptions when they can't be used or have a different meaning than the "normal" function-call syntax with parenthesis (calls a different overload).
It e.g. fits nicely with standard library-types like std::vector:
vector<int> a{10, 20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10, 20); //Parentheses -> uses arguments to parametrize some functionality,
vector<int> c(it1, it2); //like filling the vector with 10 integers or copying a range.
vector<int> d{}; //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements
There are MANY reasons to use brace initialization, but you should be aware that the initializer_list<> constructor is preferred to the other constructors, the exception being the default-constructor. This leads to problems with constructors and templates where the type T constructor can be either an initializer list or a plain old ctor.
struct Foo {
Foo() {}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}
Assuming you don't encounter such classes there is little reason not to use the intializer list.
Update (2022-02-11):
Note that there are more recent opinions on that subject to the one originally posted (below), which argue against the preference of the {} initializer, such as Arthur Dwyer in his blog post on The Knightmare of Initialization in C++.
Original Answer:
Read Herb Sutter's (updated) GotW #1.
This explains in detail the difference between these, and a few more options, along with several gotchas that are relevant for distinguishing the behavior of the different options.
The gist/copied from section 4:
When should you use ( ) vs. { } syntax to initialize objects? Why?
Here’s the simple guideline:
Guideline: Prefer to use initialization with { }, such as vector
v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because
it’s more consistent, more correct, and avoids having to know about
old-style pitfalls at all. In single-argument cases where you prefer
to see only the = sign, such as int i = 42; and auto x = anything;
omitting the braces is fine. …
That covers the vast majority of cases. There is only one main
exception:
… In rare cases, such as vector v(10,20); or auto v =
vector(10,20);, use initialization with ( ) to explicitly call a
constructor that is otherwise hidden by an initializer_list
constructor.
However, the reason this should be generally “rare” is because default
and copy construction are already special and work fine with { }, and
good class design now mostly avoids the resort-to-( ) case for
user-defined constructors because of this final design guideline:
Guideline: When you design a class, avoid providing a constructor that
ambiguously overloads with an initializer_list constructor, so that
users won’t need to use ( ) to reach such a hidden constructor.
Also see the Core Guidelines on that subject: ES.23: Prefer the {}-initializer syntax.
It only safer as long as you don't build with -Wno-narrowing like say Google does in Chromium. If you do, then it is LESS safe. Without that flag the only unsafe cases will be fixed by C++20 though.
Note:
A) Curly brackets are safer because they don't allow narrowing.
B) Curly brackers are less safe because they can bypass private or deleted constructors, and call explicit marked constructors implicitly.
Those two combined means they are safer if what is inside is primitive constants, but less safe if they are objects (though fixed in C++20)

What is the difference between using braces( ) or parentheses{ } for dynamic array initialisation? [duplicate]

MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?
Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":
List initialization does not allow narrowing (§iso.8.5.4). That is:
An integer cannot be converted to another integer that cannot hold its value. For example, char
to int is allowed, but not int to char.
A floating-point value cannot be converted to another floating-point type that cannot hold its
value. For example, float to double is allowed, but not double to float.
A floating-point value cannot be converted to an integer type.
An integer value cannot be converted to a floating-point type.
Example:
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
The only situation where = is preferred over {} is when using auto keyword to get the type determined by the initializer.
Example:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Conclusion
Prefer {} initialization over alternatives unless you have a strong reason not to.
There are already great answers about the advantages of using list initialization, however my personal rule of thumb is NOT to use curly braces whenever possible, but instead make it dependent on the conceptual meaning:
If the object I'm creating conceptually holds the values I'm passing in the constructor (e.g. containers, POD structs, atomics, smart pointers etc.), then I'm using the braces.
If the constructor resembles a normal function call (it performs some more or less complex operations that are parametrized by the arguments) then I'm using the normal function call syntax.
For default initialization I always use curly braces.
For one, that way I'm always sure that the object gets initialized irrespective of whether it e.g. is a "real" class with a default constructor that would get called anyway or a builtin / POD type. Second it is - in most cases - consistent with the first rule, as a default initialized object often represents an "empty" object.
In my experience, this ruleset can be applied much more consistently than using curly braces by default, but having to explicitly remember all the exceptions when they can't be used or have a different meaning than the "normal" function-call syntax with parenthesis (calls a different overload).
It e.g. fits nicely with standard library-types like std::vector:
vector<int> a{10, 20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10, 20); //Parentheses -> uses arguments to parametrize some functionality,
vector<int> c(it1, it2); //like filling the vector with 10 integers or copying a range.
vector<int> d{}; //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements
There are MANY reasons to use brace initialization, but you should be aware that the initializer_list<> constructor is preferred to the other constructors, the exception being the default-constructor. This leads to problems with constructors and templates where the type T constructor can be either an initializer list or a plain old ctor.
struct Foo {
Foo() {}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}
Assuming you don't encounter such classes there is little reason not to use the intializer list.
Update (2022-02-11):
Note that there are more recent opinions on that subject to the one originally posted (below), which argue against the preference of the {} initializer, such as Arthur Dwyer in his blog post on The Knightmare of Initialization in C++.
Original Answer:
Read Herb Sutter's (updated) GotW #1.
This explains in detail the difference between these, and a few more options, along with several gotchas that are relevant for distinguishing the behavior of the different options.
The gist/copied from section 4:
When should you use ( ) vs. { } syntax to initialize objects? Why?
Here’s the simple guideline:
Guideline: Prefer to use initialization with { }, such as vector
v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because
it’s more consistent, more correct, and avoids having to know about
old-style pitfalls at all. In single-argument cases where you prefer
to see only the = sign, such as int i = 42; and auto x = anything;
omitting the braces is fine. …
That covers the vast majority of cases. There is only one main
exception:
… In rare cases, such as vector v(10,20); or auto v =
vector(10,20);, use initialization with ( ) to explicitly call a
constructor that is otherwise hidden by an initializer_list
constructor.
However, the reason this should be generally “rare” is because default
and copy construction are already special and work fine with { }, and
good class design now mostly avoids the resort-to-( ) case for
user-defined constructors because of this final design guideline:
Guideline: When you design a class, avoid providing a constructor that
ambiguously overloads with an initializer_list constructor, so that
users won’t need to use ( ) to reach such a hidden constructor.
Also see the Core Guidelines on that subject: ES.23: Prefer the {}-initializer syntax.
It only safer as long as you don't build with -Wno-narrowing like say Google does in Chromium. If you do, then it is LESS safe. Without that flag the only unsafe cases will be fixed by C++20 though.
Note:
A) Curly brackets are safer because they don't allow narrowing.
B) Curly brackers are less safe because they can bypass private or deleted constructors, and call explicit marked constructors implicitly.
Those two combined means they are safer if what is inside is primitive constants, but less safe if they are objects (though fixed in C++20)

What is the difference bettwen using "()" and "{}" to initialize a variable? [duplicate]

The C++ reference pages say that () is for value initialisation, {} is for value and aggregate and list initialisation. So, if I just want value initialisation, which one do I use? () or {}? I'm asking because in the book "A Tour of C++" by Bjarne himself, he seems to prefer using {}, even for value initialisation (see for example pages 6 and 7), and so I thought it was good practice to always use {}, even for value initialisation. However, I've been badly bitten by the following bug recently. Consider the following code.
auto p = std::make_shared<int>(3);
auto q{ p };
auto r(p);
Now according to the compiler (Visual Studio 2013), q has type std::initializer_list<std::shared_ptr<int>>, which is not what I intended. What I actually intended for q is actually what r is, which is std::shared_ptr<int>. So in this case, I should not use {} for value initialisation, but use (). Given this, why does Bjarne in his book still seem to prefer to use {} for value initialisation? For example, he uses double d2{2.3} at the bottom of page 6.
To definitively answer my questions, when should I use () and when should I use {}? And is it a matter of syntax correctness or a matter of good programming practice?
Oh and uh, plain English if possible please.
EDIT:
It seems that I've slightly misunderstood value initialisation (see answers below). However the questions above still stands by and large.
Scott Meyers has a fair amount to say about the difference between the two methods of initialization in his book Effective Modern C++.
He summarizes both approaches like this:
Most developers end up choosing one kind of delimiter as a default, using
the other only when they have to. Braces-by-default folks are
attracted by their unrivaled breadth of applicability, their
prohibition of narrowing conversions, and their immunity to C++’s most
vexing parse. Such folks understand that in some cases (e.g., creation
of a std::vector with a given size and initial element value),
parentheses are required. On the other hand, the go-parentheses-go
crowd embraces parentheses as their default argument delimiter.
They’re attracted to its consistency with the C++98 syntactic
tradition, its avoidance of the auto-deduced-a-std::initializer_list
problem, and the knowledge that their object creation calls won’t be
inadvertently waylaid by std::initializer_list constructors. They
concede that sometimes only braces will do (e.g., when creating a
container with particular values). There’s no consensus that either
approach is better than the other, so my advice is to pick one and
apply it consistently.
This is my opinion.
When using auto as type specifier, it's cleaner to use:
auto q = p; // Type of q is same as type of p
auto r = {p}; // Type of r is std::initializer_list<...>
When using explicit type specifier, it's better to use {} instead of ().
int a{}; // Value initialized to 0
int b(); // Declares a function (the most vexing parse)
One could use
int a = 0; // Value initialized to 0
However, the form
int a{};
can be used to value initialize objects of user defined types too. E.g.
struct Foo
{
int a;
double b;
};
Foo f1 = 0; // Not allowed.
Foo f1{}; // Zero initialized.
First off, there seems to a terminology mixup. What you have is not value initialisation. Value initialisation happens when you do not provide any explicit initialisation arguments. int x; uses default initialisation, the value of x will be unspecified. int x{}; uses value initialisation, x will be 0. int x(); declares a function—that's why {} is preferred for value initialisation.
The code you've shown does not use value initialisation. With auto, the safest thing is to use copy initialisation:
auto q = p;
There is another important difference: The brace initializer requires that the given type can actually hold the given value. In other words, it forbids narrowing of the value, like rounding or truncation.
int a(2.3); // ok? a will hold the value 2, no error, maybe compiler warning
uint8_t c(256); // ok? the compiler should warn about something fishy going on
As compared to the brace initialization
int A{2.3}; // compiler error, because int can NOT hold a floating point value
double B{2.3}; // ok, double can hold this value
uint8_t C{256}; // compiler error, because 8bit is not wide enough for this number
Especially in generic programming with templates you should therefore use brace initialization to avoid nasty surprises when the underlying type does something unexpected to your input values.
{} is value initialization if empty, if not it is list/aggregate initialization.
From the draft, 7.1.6.4 auto specifier, 7/... Example,
auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
Rules are a little bit complex to explain here (even hard to read from the source!).
Herb Sutter seems to be making an argument in CppCon 2014 (39:25 into the talk) for using auto and brace initializers, like so:
auto x = MyType { initializers };
whenever you want to coerce the type, for left-to-right consistency in definitions:
Type-deduced: auto x = getSomething()
Type-coerced: auto x = MyType { blah }
User-defined literals auto x = "Hello, world."s
Function declaration: auto f { some; commands; } -> MyType
Named Lambda: using auto f = [=]( { some; commands; } -> MyType
C++11-style typedef: using AnotherType = SomeTemplate<MyTemplateArg>
Scott Mayers just posted a relevant blog entry Thoughts on the Vagaries of C++ Initialization. It seems that C++ still has a way to go before achieving a truly uniform initialization syntax.

What do curly braces mean in C++ when initializing [duplicate]

MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?
Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":
List initialization does not allow narrowing (§iso.8.5.4). That is:
An integer cannot be converted to another integer that cannot hold its value. For example, char
to int is allowed, but not int to char.
A floating-point value cannot be converted to another floating-point type that cannot hold its
value. For example, float to double is allowed, but not double to float.
A floating-point value cannot be converted to an integer type.
An integer value cannot be converted to a floating-point type.
Example:
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
The only situation where = is preferred over {} is when using auto keyword to get the type determined by the initializer.
Example:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Conclusion
Prefer {} initialization over alternatives unless you have a strong reason not to.
There are already great answers about the advantages of using list initialization, however my personal rule of thumb is NOT to use curly braces whenever possible, but instead make it dependent on the conceptual meaning:
If the object I'm creating conceptually holds the values I'm passing in the constructor (e.g. containers, POD structs, atomics, smart pointers etc.), then I'm using the braces.
If the constructor resembles a normal function call (it performs some more or less complex operations that are parametrized by the arguments) then I'm using the normal function call syntax.
For default initialization I always use curly braces.
For one, that way I'm always sure that the object gets initialized irrespective of whether it e.g. is a "real" class with a default constructor that would get called anyway or a builtin / POD type. Second it is - in most cases - consistent with the first rule, as a default initialized object often represents an "empty" object.
In my experience, this ruleset can be applied much more consistently than using curly braces by default, but having to explicitly remember all the exceptions when they can't be used or have a different meaning than the "normal" function-call syntax with parenthesis (calls a different overload).
It e.g. fits nicely with standard library-types like std::vector:
vector<int> a{10, 20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10, 20); //Parentheses -> uses arguments to parametrize some functionality,
vector<int> c(it1, it2); //like filling the vector with 10 integers or copying a range.
vector<int> d{}; //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements
There are MANY reasons to use brace initialization, but you should be aware that the initializer_list<> constructor is preferred to the other constructors, the exception being the default-constructor. This leads to problems with constructors and templates where the type T constructor can be either an initializer list or a plain old ctor.
struct Foo {
Foo() {}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}
Assuming you don't encounter such classes there is little reason not to use the intializer list.
Update (2022-02-11):
Note that there are more recent opinions on that subject to the one originally posted (below), which argue against the preference of the {} initializer, such as Arthur Dwyer in his blog post on The Knightmare of Initialization in C++.
Original Answer:
Read Herb Sutter's (updated) GotW #1.
This explains in detail the difference between these, and a few more options, along with several gotchas that are relevant for distinguishing the behavior of the different options.
The gist/copied from section 4:
When should you use ( ) vs. { } syntax to initialize objects? Why?
Here’s the simple guideline:
Guideline: Prefer to use initialization with { }, such as vector
v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because
it’s more consistent, more correct, and avoids having to know about
old-style pitfalls at all. In single-argument cases where you prefer
to see only the = sign, such as int i = 42; and auto x = anything;
omitting the braces is fine. …
That covers the vast majority of cases. There is only one main
exception:
… In rare cases, such as vector v(10,20); or auto v =
vector(10,20);, use initialization with ( ) to explicitly call a
constructor that is otherwise hidden by an initializer_list
constructor.
However, the reason this should be generally “rare” is because default
and copy construction are already special and work fine with { }, and
good class design now mostly avoids the resort-to-( ) case for
user-defined constructors because of this final design guideline:
Guideline: When you design a class, avoid providing a constructor that
ambiguously overloads with an initializer_list constructor, so that
users won’t need to use ( ) to reach such a hidden constructor.
Also see the Core Guidelines on that subject: ES.23: Prefer the {}-initializer syntax.
It only safer as long as you don't build with -Wno-narrowing like say Google does in Chromium. If you do, then it is LESS safe. Without that flag the only unsafe cases will be fixed by C++20 though.
Note:
A) Curly brackets are safer because they don't allow narrowing.
B) Curly brackers are less safe because they can bypass private or deleted constructors, and call explicit marked constructors implicitly.
Those two combined means they are safer if what is inside is primitive constants, but less safe if they are objects (though fixed in C++20)

Why is {} initialization in C++ safer than other methods? [duplicate]

MyClass a1 {a}; // clearer and less error-prone than the other three
MyClass a2 = {a};
MyClass a3 = a;
MyClass a4(a);
Why?
Basically copying and pasting from Bjarne Stroustrup's "The C++ Programming Language 4th Edition":
List initialization does not allow narrowing (§iso.8.5.4). That is:
An integer cannot be converted to another integer that cannot hold its value. For example, char
to int is allowed, but not int to char.
A floating-point value cannot be converted to another floating-point type that cannot hold its
value. For example, float to double is allowed, but not double to float.
A floating-point value cannot be converted to an integer type.
An integer value cannot be converted to a floating-point type.
Example:
void fun(double val, int val2) {
int x2 = val; // if val == 7.9, x2 becomes 7 (bad)
char c2 = val2; // if val2 == 1025, c2 becomes 1 (bad)
int x3 {val}; // error: possible truncation (good)
char c3 {val2}; // error: possible narrowing (good)
char c4 {24}; // OK: 24 can be represented exactly as a char (good)
char c5 {264}; // error (assuming 8-bit chars): 264 cannot be
// represented as a char (good)
int x4 {2.0}; // error: no double to int value conversion (good)
}
The only situation where = is preferred over {} is when using auto keyword to get the type determined by the initializer.
Example:
auto z1 {99}; // z1 is an int
auto z2 = {99}; // z2 is std::initializer_list<int>
auto z3 = 99; // z3 is an int
Conclusion
Prefer {} initialization over alternatives unless you have a strong reason not to.
There are already great answers about the advantages of using list initialization, however my personal rule of thumb is NOT to use curly braces whenever possible, but instead make it dependent on the conceptual meaning:
If the object I'm creating conceptually holds the values I'm passing in the constructor (e.g. containers, POD structs, atomics, smart pointers etc.), then I'm using the braces.
If the constructor resembles a normal function call (it performs some more or less complex operations that are parametrized by the arguments) then I'm using the normal function call syntax.
For default initialization I always use curly braces.
For one, that way I'm always sure that the object gets initialized irrespective of whether it e.g. is a "real" class with a default constructor that would get called anyway or a builtin / POD type. Second it is - in most cases - consistent with the first rule, as a default initialized object often represents an "empty" object.
In my experience, this ruleset can be applied much more consistently than using curly braces by default, but having to explicitly remember all the exceptions when they can't be used or have a different meaning than the "normal" function-call syntax with parenthesis (calls a different overload).
It e.g. fits nicely with standard library-types like std::vector:
vector<int> a{10, 20}; //Curly braces -> fills the vector with the arguments
vector<int> b(10, 20); //Parentheses -> uses arguments to parametrize some functionality,
vector<int> c(it1, it2); //like filling the vector with 10 integers or copying a range.
vector<int> d{}; //empty braces -> default constructs vector, which is equivalent
//to a vector that is filled with zero elements
There are MANY reasons to use brace initialization, but you should be aware that the initializer_list<> constructor is preferred to the other constructors, the exception being the default-constructor. This leads to problems with constructors and templates where the type T constructor can be either an initializer list or a plain old ctor.
struct Foo {
Foo() {}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer list" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
};
int main() {
Foo a;
Foo b(a); // copy ctor
Foo c{a}; // copy ctor (init. list element) + initializer list!!!
}
Assuming you don't encounter such classes there is little reason not to use the intializer list.
Update (2022-02-11):
Note that there are more recent opinions on that subject to the one originally posted (below), which argue against the preference of the {} initializer, such as Arthur Dwyer in his blog post on The Knightmare of Initialization in C++.
Original Answer:
Read Herb Sutter's (updated) GotW #1.
This explains in detail the difference between these, and a few more options, along with several gotchas that are relevant for distinguishing the behavior of the different options.
The gist/copied from section 4:
When should you use ( ) vs. { } syntax to initialize objects? Why?
Here’s the simple guideline:
Guideline: Prefer to use initialization with { }, such as vector
v = { 1, 2, 3, 4 }; or auto v = vector{ 1, 2, 3, 4 };, because
it’s more consistent, more correct, and avoids having to know about
old-style pitfalls at all. In single-argument cases where you prefer
to see only the = sign, such as int i = 42; and auto x = anything;
omitting the braces is fine. …
That covers the vast majority of cases. There is only one main
exception:
… In rare cases, such as vector v(10,20); or auto v =
vector(10,20);, use initialization with ( ) to explicitly call a
constructor that is otherwise hidden by an initializer_list
constructor.
However, the reason this should be generally “rare” is because default
and copy construction are already special and work fine with { }, and
good class design now mostly avoids the resort-to-( ) case for
user-defined constructors because of this final design guideline:
Guideline: When you design a class, avoid providing a constructor that
ambiguously overloads with an initializer_list constructor, so that
users won’t need to use ( ) to reach such a hidden constructor.
Also see the Core Guidelines on that subject: ES.23: Prefer the {}-initializer syntax.
It only safer as long as you don't build with -Wno-narrowing like say Google does in Chromium. If you do, then it is LESS safe. Without that flag the only unsafe cases will be fixed by C++20 though.
Note:
A) Curly brackets are safer because they don't allow narrowing.
B) Curly brackers are less safe because they can bypass private or deleted constructors, and call explicit marked constructors implicitly.
Those two combined means they are safer if what is inside is primitive constants, but less safe if they are objects (though fixed in C++20)