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);
std::vector<int> b{3, 1};
struct Config { int retries = 3; bool verbose = false; };
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;
std::pair p{1, 2.0};
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);
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;
int scratch [[indeterminate]];
C++29direction · not adopted
Direction: expanding erroneous behavior to more categories of “forgot to initialize / forgot to check” bugs is an active Evolution thread.