Question

We can initialize the variable in two ways in C++

One:

int abc = 7;

Two:

int abc {7};

What is the difference between these two methods? Does the compiler treat them differently, or is there a difference in the way the code is executed?

Was it helpful?

Solution

Short version

Initialization via {..} is list-initialization, which prohibits narrowing conversions. For example, if LLONG_MAX is the maximum value of an long long int, and your int cannot represent that:

int x = LLONG_MAX;  // probably accepted with a warning
int x {LLONG_MAX};  // error

Similarly:

long long y = /*something*/;

int x = y;  // accepted, maybe with a warning
int x {y};  // error

Long version

An initialization of the form

T x = a;

is copy-initialization; an initialization of either form

T x(a);
T x{a};

is direct-initialization, [dcl.init]/15-16.

[dcl.init]/14 then says:

The form of initialization (using parentheses or =) is generally insignificant, but does matter when the initializer or the entity being initialized has a class type; see below.

So for non-class types, the form of the initialization doesn't matter. However, there's a difference between these two direct-initializations:

T x(a);  // 1
T x{a};  // 2

and similarly, between these two copy-initializations:

T x = a;    // 1
T x = {a};  // 2

Namely, the ones with {..} use list-initialization. The {..} is called a braced-init-list.

So, when you compare T x = a; to T x {a};, there are two differences: copy- vs. direct-initialization, and "non-list-" vs. list-initialization. As already mentioned by others and in the quote above, for non-class types T, there's no difference between copy- and direct-init. However, there's a difference between list-init and no list-init. That is, we could as well compare

int x (a);
int x {a};

List-initialization in this case prohibits narrowing conversions. Narrowing conversions are defined in [dcl.init.list]/7 as:

A narrowing conversion is an implicit conversion

  • from a floating-point type to an integer type, or

  • from long double to double or float, or from double to float, except where the source is a constant expression and the actual value after conversion is within the range of values that can be represented (even if it cannot be represented exactly), or

  • from an integer type or unscoped enumeration type to a floating-point type, except where the source is a constant expression and the actual value after conversion will fit into the target type and will produce the original value when converted back to the original type, or

  • from an integer type or unscoped enumeration type to an integer type that cannot represent all the values of the original type, except where the source is a constant expression whose value after integral promotions will fit into the target type.

OTHER TIPS

While for int the existing replies are complete, I painfully found out, that in some cases, there are other differences between the () and {} initializations.

The keyword is that {} is an initializer list.

One such cases is, the std::string initialization with count copies of a char:

std::string stars(5, '*')

will initialize stars as *****, but

std::string stars{5, '*'}

will be read as std::string stars(char(5), '*') and initialize star as * (preceded by an hidden character).

The first is the copy initialization, while the second is list initialization.

But, usually copy initialization is less used. Because, if you're doing it by passing objects of user defined types, it just causes bitcopy & hence may not produce intended results if the user defined class uses pointers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top