Search Knowledge

© 2026 LIBREUNI PROJECT

Modern C++ Programming / Modern C++20/23

Designated Initializers (C++20)

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

  1. Self-documenting: Code clearly shows what each value represents
  2. Refactor-safe: Adding/removing/reordering members is safer
  3. Default values: Easy to use defaults for some members
  4. Type safety: Compiler catches mismatched types
  5. IDE support: Better autocomplete and hints

Limitations

  1. Must be in declaration order (unlike C99)
  2. Only works with aggregates
  3. Cannot mix with positional initialization
  4. Cannot repeat members
  5. 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...