Initialization

Core semantics7 standards

Braces vs. parens, NSDMI, designated initializers, EB.

Brace initialization arrives with the famous trap: initializer_list constructors win. NSDMI (default member initializers) become the first line of defense against uninitialized members.

std::vector<int> a(3, 1);   // {1, 1, 1}
std::vector<int> b{3, 1};   // {3, 1} — initializer_list wins

struct Config { int retries = 3; bool verbose = false; };  // NSDMI

Aggregates may have NSDMI again (C++11 banned the combination), so plain structs get safe defaults without writing constructors.

CTAD removes the make_pair/make_tuple ritual; inline constexpr variables give headers one-definition constants.

inline constexpr int max_clients = 64;    // in a header, one entity
std::pair p{1, 2.0};                       // CTAD

Designated initializers make aggregate call sites self-documenting (in declaration order), and parentheses now also initialize aggregates, fixing make_unique<Aggregate>(...).

Config c{.retries = 5, .verbose = true};
auto p = std::make_unique<Point>(3, 4);   // works since C++20

Quiet release for initialization itself — the C++20 guidance (braces by default, designated initializers for aggregates, NSDMI everywhere) stands unchanged.

Reading an uninitialized automatic variable becomes erroneous behavior (P2795): defined to be diagnosable instead of full UB, with [[indeterminate]] to opt back out. “Always initialize” is now also the compiler's position.

int n;                 // erroneous to read before assignment (diagnosed)
int scratch [[indeterminate]];   // explicit opt-out for hot paths
C++29direction · not adopted

Direction: expanding erroneous behavior to more categories of “forgot to initialize / forgot to check” bugs is an active Evolution thread.

C++26 per the final working draft; C++29 rows are proposals and direction, not adopted features.