Initialization difference with or without curly braces in C++

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.

Leave a Comment