Introduction
Good practices are particularly important when programming in C and C++, as they improve code quality and robustness.
Designated Initializers
Designated initializers, which have been available in C99 for over two decades, were introduced in C++20. They allow you to initialize struct or aggregate members by name instead of by position. The basic syntax is:
struct PeriodicTaskInfo {
std::chrono::milliseconds _computationTime;
std::chrono::milliseconds _period;
zpp_lib::PreemptableThreadPriority _priority;
const char* _szTaskName;
};
// without designated initializers
PeriodicTaskInfo taskInfo1 = { 10ms, 50ms, zpp_lib::PreemptableThreadPriority::PriorityHigh, "Engine" };
// with designated initializers
PeriodicTaskInfo taskInfo2 = {
._computationTime = 10ms,
._period = 50ms,
._priority = zpp_lib::PreemptableThreadPriority::PriorityHigh,
._szTaskName = "Engine" };
The arguments for using designated initializers are:
- Improved readability: The initializer is self-documenting. With positional initialization alone, the reader must look up the struct definition to understand the meaning of each value. This significantly improves code review.
- Resilience to
structevolution: Adding a field to a struct modifies the memory layout. If one uses positional initializers only, this may assign completely incorrect values to fields. The same applies when a field is moved in thestructdeclaration:// Modified TaskInfo structure where the two first fields are swapped // All positional initializations will compile without error but with wrong values struct PeriodicTaskInfo { std::chrono::milliseconds _period; std::chrono::milliseconds _computationTime; zpp_lib::PreemptableThreadPriority _priority; const char* _szTaskName; }; -
Partial initialization with explicit defaults is possible with designated initializers. Additionally,
structinstances can be initialized with fields that take their default values:struct PeriodicTaskInfo { std::chrono::milliseconds _computationTime; std::chrono::milliseconds _period; zpp_lib::PreemptableThreadPriority _priority; const char* _szTaskName; bool _enabled = true; }; PeriodicTaskInfo taskInfo1 = { ._computationTime = 10ms, ._period = 50ms, ._priority = zpp_lib::PreemptableThreadPriority::PriorityHigh, ._szTaskName = "Engine" }; }; PeriodicTaskInfo taskInfo2 = { ._computationTime = 10ms, ._period = 50ms, ._priority = zpp_lib::PreemptableThreadPriority::PriorityLow, ._szTaskName = "DisabledTask", ._enabled = false }; } -
Consistency between C and C++. C99 designated initializers have been the standard for over 25 years and are widely used in embedded codebases.