Designated Initializers
C++20 introduces designated initializers from C99, allowing you to initialize struct members by name. This improves clarity and maintainability, especially for structs with many members.
Basic Syntax
struct Config {
int width;
int height;
bool fullscreen;
double scale;
};
// Traditional initialization
Config c1{1920, 1080, true, 2.0}; // What does each value mean?
// C++20 designated initializers
Config c2{
.width = 1920,
.height = 1080,
.fullscreen = true,
.scale = 2.0
}; // Crystal clear!
Partial Initialization
You can initialize only specific members:
struct Settings {
int timeout = 30; // Default value
bool debug = false;
std::string host = "localhost";
};
Settings s{.debug = true}; // Only set debug, others use defaults
// timeout=30, debug=true, host="localhost"
Order Requirement
C++ requires designated initializers to match declaration order:
struct Point {
int x;
int y;
int z;
};
Point p1{.x = 1, .y = 2, .z = 3}; // OK: follows declaration order
Point p2{.x = 1, .z = 3, .y = 2}; // ERROR: out of order!
Point p3{.y = 2, .z = 3}; // OK: can skip x
This differs from C99, which allows any order.
Cannot Mix with Positional
struct Data {
int a, b, c;
};
Data d1{1, .b = 2, .c = 3}; // ERROR: cannot mix
Data d2{.a = 1, 2, 3}; // ERROR: cannot mix
Data d3{.a = 1, .b = 2}; // OK: all designated
Data d4{1, 2, 3}; // OK: all positional
Nested Structs
Designated initializers work with nested structures:
struct Address {
std::string street;
std::string city;
int zipcode;
};
struct Person {
std::string name;
int age;
Address address;
};
Person p{
.name = "Alice",
.age = 30,
.address = {
.street = "123 Main St",
.city = "Springfield",
.zipcode = 12345
}
};
Arrays in Structs
struct Palette {
unsigned char colors[3];
std::string name;
};
Palette red{
.colors = {255, 0, 0},
.name = "Red"
};
With Constructor Overloading
Designated initializers use aggregate initialization, so they work only with aggregates:
struct Simple {
int x, y;
// No user-defined constructors
};
Simple s{.x = 1, .y = 2}; // OK: aggregate
struct WithConstructor {
int x, y;
WithConstructor(int a, int b) : x(a), y(b) {}
};
WithConstructor w{.x = 1, .y = 2}; // ERROR: not an aggregate
std::array and Containers
Designated initializers don’t work directly with std::array, but you can use them in enclosing structs:
struct Config {
std::array<int, 3> values;
std::string name;
};
Config cfg{
.values = {1, 2, 3},
.name = "Test"
};
Real-World Example
Configuration management becomes much clearer:
struct ServerConfig {
std::string host = "0.0.0.0";
int port = 8080;
int max_connections = 100;
bool enable_tls = false;
std::string cert_path;
int timeout_seconds = 30;
bool verbose = false;
};
// Clear what each setting does
ServerConfig production{
.host = "api.example.com",
.port = 443,
.enable_tls = true,
.cert_path = "/etc/ssl/cert.pem",
.verbose = false
};
ServerConfig development{
.port = 3000,
.verbose = true
};
Function Parameters
Particularly useful for options objects:
struct DrawOptions {
int line_width = 1;
unsigned color = 0x000000;
bool anti_alias = true;
double opacity = 1.0;
};
void draw_line(Point start, Point end, DrawOptions opts = {});
// Call with clear intent
draw_line(
{.x = 0, .y = 0},
{.x = 100, .y = 100},
{.color = 0xFF0000, .line_width = 3}
);
Comparison: Before and After
C++17
struct WindowConfig {
int width, height;
std::string title;
bool resizable, fullscreen;
};
// Hard to read
WindowConfig cfg{800, 600, "My App", true, false};
// Better, but verbose
WindowConfig cfg;
cfg.width = 800;
cfg.height = 600;
cfg.title = "My App";
cfg.resizable = true;
cfg.fullscreen = false;
C++20
WindowConfig cfg{
.width = 800,
.height = 600,
.title = "My App",
.resizable = true,
.fullscreen = false
};
With const and constexpr
struct Constants {
int max_retries;
double timeout;
};
constexpr Constants network_defaults{
.max_retries = 3,
.timeout = 5.0
};
Benefits
- Self-documenting: Code clearly shows what each value represents
- Refactor-safe: Adding/removing/reordering members is safer
- Default values: Easy to use defaults for some members
- Type safety: Compiler catches mismatched types
- IDE support: Better autocomplete and hints
Limitations
- Must be in declaration order (unlike C99)
- Only works with aggregates
- Cannot mix with positional initialization
- Cannot repeat members
- No computed field names
Conceptual Check
What is required about the order of designated initializers in C++20?
Interactive Lab
Complete the Code
struct Rectangle { int width = 10; int height = 20; std::string color = "red"; }; // Initialize rect with width=50, height=30, keep default color Rectangle rect{};
Runtime Environment
Interactive Lab
1#include <iostream>
2#include <string>
3
4struct Employee {
5 std::string name;
6 int id;
7 double salary = 50000.0;
8 bool remote = false;
9};
10
11int main() {
12 Employee e1{
13 .name = "Alice",
14 .id = 101,
15 .salary = 75000.0
16 };
17
18 Employee e2{
19 .name = "Bob",
20 .id = 102,
21 .remote = true
22 };
23
24 std::cout << e1.name << ": $" << e1.salary << ", Remote: " << e1.remote << '\n';
25 std::cout << e2.name << ": $" << e2.salary << ", Remote: " << e2.remote << '\n';
26
27 return 0;
28}
System Console
Waiting for signal...