initialization: parenthesis vs. equals sign
T a( b );
is direct initialization, unless it parses as a function declaration, in which case it's a function declaration.
T a = b;
is copy initialization, which means that it works as if a temporary object is constructed on the right hand side, and that a
is then copy constructed or, in C++11 and later, possibly move constructed, from that temporary.
The compiler is free to elide (remove) the temporary+copying/moving whenever it can, but a copy or move constructor, whichever would be logically used, must still be accessible and not explicit
.
For example, in C++03 you cannot copy-initialize a std::ostringstream
, because it doesn't have a copy constructor. In C++11 you can copy-initialize an ostringstream
if the initializer is a temporary, which then results in a logical move construction (which however will usually be elided, optimized away). For example, this copy initialization declaration,
ostringstream s = ostringstream( "blah" );
… doesn't compile as C++03, because in C++03 the copy initialization invokes the class' copy constructor, which doesn't exist. It does however compile as C++11, because in C++11 the copy initialization invokes the move constructor. And while (to maintain its illusion of being a stream) a std::ostringstream
can't be directly copied, it can be moved.
Another such difference: in C++03 only the copy initialization syntax supports curly braces initializer, which in C++03 you can use when T
is an aggregate type such as a raw array. In C++11 the curly braces notation has been extended and generalized as a uniform initialization syntax, so it can be used also with direct initialization. And so the following direct initialization declaration,
int v[]{ 3, 1, 4, 1, 5, 9, 2, 6, 5, 4 };
… does not compile as C++03, but does compile as C++11 and later.
The =
copy initialization syntax is the original initialization syntax from C.
And in C++11 and later, due to move semantics, it can be used in a much wider range of cases than in C++03, such as with a std::ostringstream
.
Use curly braces({}) or equal sign(=) when initialize a variable
Which one you choose depends on your own coding style and what you think is best. The most important thing is once you decide which method to use, use that method consistently. Don't switch between methods, it can make it very confusing to read your code. An additional style of variable initialization since C++98 (Called "direct initialization") is:
int variable(1)
But I would advise you against doing this, it doesn't work in certain circumstances, as your book may cover.
My personal style is the one my grandfather who worked on IBM mainframes in the 1960's taught me:
int
Variable1 = 2,
Variable2 = 39,
Variable3 = 45;
bool
Foo = true,
Bar = false;
// etc.
You'll notice I use the "=" sign over curly braces too. This seems to be how the majority of people write their code so me and my Grandfather write it that way to reduce confusion when people read our code. How accepted this method is in a corporate setting or in an organization I do not know, I simply thought it was the most attractive and intuitive style. It also saves a lot of typing.
Does the equal sign make a difference in brace initialization? eg. 'T a = {}' vs 'T a{}'
The only significant difference I know is in the treatment of explicit
constructors:
struct foo
{
explicit foo(int);
};
foo f0 {42}; // OK
foo f1 = {42}; // not allowed
This is similar to the "traditional" initialization:
foo f0 (42); // OK
foo f1 = 42; // not allowed
See [over.match.list]/1.
Apart from that, there's a defect (see CWG 1270) in C++11 that allows brace-elision only for the form T a = {something}
struct aggr
{
int arr[5];
};
aggr a0 = {1,2,3,4,5}; // OK
aggr a1 {1,2,3,4,5}; // not allowed
When should we use parenthesis ( ) vs. initializer { } syntax to initialize objects in C++11?
Scott Meyers tackles this issue in Item 7 of his fantastic "Effective Modern C++". He runs through the differences, pros and cons of both syntaxes, and concludes
There’s no consensus that either approach is better than the other, so my advice is to pick one and apply it consistently.
On the other hand, the C++ Core Guidelines suggest that you prefer the initialiser syntax, so perhaps that's the better default to go for.
Difference between = and {} syntaxes for initializing a variable in C++
int score = 0;
performs copy initialization, as the effect, score
is initialized to the specified value 0
.
Otherwise (if neither
T
nor the type ofother
are class types), standard conversions are used, if necessary, to convert the value ofother
to the cv-unqualified version ofT
.
int score {};
performs value initialization with braced initializer, which was supported since C++11, as the effect,
otherwise, the object is zero-initialized.
score
is of built-in type int
, it's zero-initialized at last, i.e. initialized to 0
.
If
T
is a scalar type, the object's initial value is the integral constant zero explicitly converted toT
.
Difference between int i(x); and int i = x;
The answer is essentially here.
int i(x);
is direct initialization which
Initializes an object from explicit set of constructor arguments.
whereas int i = x;
is copy initialization, which
Initializes an object from another object
(The following is probably irrelevant for int
, so take it as comment about the difference of the two syntaxes for actual classes: A i(x);
vs A i = x;
.)
Notice, that both syntaxes can result in a copy constructor being called: if a copy constructor exists for the class A
, then the explicit set of constructor arguments that you can provide via direct initialization can consist of exactly one object of class A
(which is by the way exactly what happens if x
is an int
/A
in int i(x);
/A i(x);
.
This is to say that copy initialization is not the only way to construct an object copying it from another object of the same class.
Another point worth noting, in my opinion, is that copy initialization doesn't necessarily mean that copy constructor will be called. If the class A
has (beside the copy constructor A(A const&)
, if you like) the constructor A(int)
, that is the constructor that will be called if you write A a = 3;
.
All this is to say that in my opinion the names of the 6 syntaxes you find listed at the first link I provided are not really telling you what happens; the same thing can happen for more syntaxes, and different things can happen for the same syntax. Those 6 are just names to refer to the syntax of the initialization, not to how the process of initialization happens, because the latter depends on the class of the initialized object and on the initializer object (and on the syntax you pick, sure).
Variable initialization, what's the difference?
C++ supports three basic ways to initialize a variable.
First, we can do copy initialization by using an equals sign:
int width = 5; // copy initialization of value 5 into variable width
This copies the value on the right-hand side of the equals to the variable being created on the left-hand side.
Second, we can do a direct initialization by using parenthesis.
int width( 5 ); // direct initialization of value 5 into variable width
For simple data types (like integers), copy and direct initialization are essentially the same. But for some advanced types, direct initialization can perform better than copy initialization.
Before C++11, direct initialization was recommended over copy initialization in most cases because of the performance boost.
Unfortunately, direct initialization can’t be used for all types of initialization. In an attempt to provide a more consistent initialization mechanism, C++11 added a new syntax for direct initialization called brace initialization (also called uniform initialization) that uses curly braces:
int width{ 5 }; // brace (uniform) initialization of value 5 into variable width
Hope this will help you.
Initialize struct using parentheses instead of curly braces and an equal sign
You can have a user-defined constructor for your struct:
#include <iostream>
struct Matrix2x2 {
int x1;
int x2;
int x3;
int x4;
Matrix2x2(int a, int b, int c, int d)
: x1(a), x2(b), x3(c), x4(d)
{}
};
int main() {
Matrix2x2 m1 = { 1, 2, 3, 4 }; // list initialization
Matrix2x2 res(1, 2, 3, 4); // calls user-defined constructor
}
And pass in the arguments when creating an object (surrounded by parentheses). But you should really prefer the braced initializer instead as it is immune to the most vexing parse:
Matrix2x2 res{ 1, 2, 3, 4 }; // calls user-defined constructor, braced initialization
What are the advantages of list initialization (using curly braces)?
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.
Related Topics
How Do Compilers Treat Variable Length Arrays
In C++, Is It Still Bad Practice to Return a Vector from a Function
When Should I Use Typedef in C++
What Is the Underlying Data Structure of a Stl Set in C++
Memory Allocation Char* and Char[]
Declare a Reference and Initialize Later
Specify Template Parameters at Runtime
Check If a Pointer Points to Allocated Memory on the Heap
Rand() Generating the Same Number - Even with Srand(Time(Null)) in My Main!
Behavior When Dereferencing the .End() of a Vector of Strings
How to Use Glortho() in Opengl
How to Get the Gl Library/Headers
Conversion Function for Error Checking Considered Good
Launch Failed. Binary Not Found. Cdt on Eclipse Helios