Designated Initializers: Finally in C++20
Designated initializers let you name the members of a struct during initialization in a way that’s checked by the compiler. To see why this is helpful, consider the following example where I accidentally swapped two parameters in the constructor:
Preventing bugs
#include <iostream>
struct Config {
int timeout;
int retries;
bool verbose;
};
int main() {
Config cfg{30, false, 1};
std::cout << "Config: timeout=" << cfg.timeout
<< ", retries=" << cfg.retries
<< ", verbose=" << std::boolalpha << cfg.verbose << "\n";
return 0;
} Believe it or not, this compiles with no warnings on GCC 15.2! false is implicitly converted to 0, and 1 is implicitly converted to true, so the swapping of retries with verbose isn’t caught.
If we had written Config cfg{.timeout=30, .verbose=false, .retries=1}, the compiler would initialize the struct correctly or trigger a compilation error depending on whether we were in C or C++.
Basic Syntax
📝 Exercise 1: Basic Designated Initializers
Initialize the Person struct using designated initializers so it prints the expected output.
📝 Exercise 1: Basic Designated Initializers
Initialize the Person struct using designated initializers so it prints the expected output.
Partial Initialization
We can also use designated initializers to do partial initialization:
#include <iostream>
struct Settings {
int volume; // guaranteed to be 0 if partially initialized
int brightness = 100; // default value
bool darkMode = true; // default value
};
int main() {
// Only override brightness, keep defaults for others
Settings s{.brightness = 75};
std::cout << "Settings: volume=" << s.volume
<< ", brightness=" << s.brightness
<< ", darkMode=" << std::boolalpha << s.darkMode << "\n";
return 0;
} If a struct member doesn’t have a default, it’ll be zero-initialized.
📝 Exercise 2: Partial Initialization
Given the NetworkConfig struct with defaults, use designated initializers to set ONLY the port to 8080.
📝 Exercise 2: Partial Initialization
Given the NetworkConfig struct with defaults, use designated initializers to set ONLY the port to 8080.
Limitations
In C++ specifically, designated initializers must be written in order of the struct members. We also can’t use designated initialization with user-provided constructors, virtual base classes, or non-public member variables.
class Widget {
int value; // private - can't use designated init
public:
Widget(int v); // user-provided constructor
};
Widget w{.value = 42}; // Won't compile
struct Point {
int x;
int y;
};
Point p{.y = 2, .x = 1}; // Won't compile in C++, will compile in C
Fun in C
C’s designated initializers are more interesting. Aside from working in any order, nested initialization and array initialization are also acceptable.
#include <stdio.h>
struct Point {
int x;
int y;
};
struct Window {
struct Point origin;
int width;
int height;
};
int main(void) {
// Nested designated initializers - valid C, invalid C++
struct Window w = {
.origin.y = 7, // Out of order!
.origin.x = 6,
.width = 67,
.height = 76
};
printf("Window: (%d, %d) size %dx%d\n",
w.origin.x, w.origin.y, w.width, w.height);
// Array initialization
int 🫲🥴🫱[10] = {[6] = 7}; // Unicode variable names????
printf("🫲🥴🫱[6] = %d", 🫲🥴🫱[6]);
return 0;
} Let’s try C out!
📝 Exercise 3: C-style designated initialization
Use C's array designators to create a sparse array with specific values at indices 2, 5, and 9. Initialize them in whatever order you want.
📝 Exercise 3: C-style designated initialization
Use C's array designators to create a sparse array with specific values at indices 2, 5, and 9. Initialize them in whatever order you want.
Got questions or cool use cases? Find me on LinkedIn!