C++14coreintermediate
Aggregate Member Initialization Relaxation
C++14 · Modern Refined · Compiler flag: -std=c++14
Explanation
C++14 relaxes aggregate initialization rules to allow classes with default member initializers (in-class brace-or-equal initializers) to still qualify as aggregates. In C++11, any default member initializer disqualified a class from aggregate initialization. C++14 removes this restriction, so you can combine both features naturally.
Real-World Scenario
A game engine defines a `RenderSettings` struct with sensible defaults for resolution, anti-aliasing, and VSync. Users can aggregate-initialize only the fields they want to override while relying on defaults for the rest.
Quick Snippet
snippet.cpp
#include <iostream>
struct RenderSettings {
int width = 1920;
int height = 1080;
bool vsync = true;
int msaa = 4;
};
int main() {
RenderSettings low_quality{1280, 720, false, 1};
RenderSettings custom{3840, 2160}; // vsync=true, msaa=4 from defaults
std::cout << custom.width << "x" << custom.height
<< " VSync:" << custom.vsync << " MSAA:" << custom.msaa << "\n";
// 3840x2160 VSync:1 MSAA:4
}Full Example
aggregate_member_init.cpp
// CodeNebula - C++14 Example: Aggregate Member Initialization
// Compile: g++ -std=c++14 -Wall -Wextra -o example aggregate_member_init.cpp
// Demonstrates relaxed aggregate initialization rules in C++14. Aggregates
// can now have default member initializers (NSDMIs) and still be initialized
// with brace syntax. In C++11, having a default member initializer made a
// class non-aggregate. Real-world use: configuration structs with sensible
// defaults, POD-like types with partial initialization.
#include <iostream>
#include <string>
// ------------------------------------------------------------------
// 1. Aggregate with default member initializers (C++14 allows this)
// ------------------------------------------------------------------
struct NetworkConfig
{
std::string host = "localhost";
int port = 8080;
int max_retries = 3;
bool use_ssl = false;
};
// ------------------------------------------------------------------
// 2. Nested aggregates with defaults
// ------------------------------------------------------------------
struct Point
{
double x = 0.0;
double y = 0.0;
};
struct Rectangle
{
Point top_left;
Point bottom_right;
std::string label = "unnamed";
};
// ------------------------------------------------------------------
// 3. Array member with default init
// ------------------------------------------------------------------
struct Sensor
{
int id = -1;
double readings[3] = {0.0, 0.0, 0.0};
bool active = true;
};
void print_config(const NetworkConfig& cfg)
{
std::cout << " host=" << cfg.host
<< " port=" << cfg.port
<< " retries=" << cfg.max_retries
<< " ssl=" << std::boolalpha << cfg.use_ssl << std::endl;
}
void print_rect(const Rectangle& r)
{
std::cout << " [" << r.label << "] ("
<< r.top_left.x << "," << r.top_left.y << ") -> ("
<< r.bottom_right.x << "," << r.bottom_right.y << ")" << std::endl;
}
int main()
{
// ------------------------------------------------------------------
// 1. Using all defaults (empty braces)
// ------------------------------------------------------------------
NetworkConfig default_cfg{};
std::cout << "=== All Defaults ===" << std::endl;
print_config(default_cfg);
std::cout << std::endl;
// ------------------------------------------------------------------
// 2. Overriding some members, rest keep defaults
// ------------------------------------------------------------------
NetworkConfig custom_cfg{"api.example.com", 443};
// max_retries=3, use_ssl=false come from defaults
std::cout << "=== Partial Override ===" << std::endl;
print_config(custom_cfg);
std::cout << std::endl;
// ------------------------------------------------------------------
// 3. Overriding all members
// ------------------------------------------------------------------
NetworkConfig full_cfg{"db.internal", 5432, 5, true};
std::cout << "=== Full Override ===" << std::endl;
print_config(full_cfg);
std::cout << std::endl;
// ------------------------------------------------------------------
// 4. Nested aggregates with defaults
// ------------------------------------------------------------------
Rectangle r1{}; // all defaults
Rectangle r2{{1.0, 2.0}, {10.0, 20.0}}; // label defaults
Rectangle r3{{0.0, 0.0}, {5.0, 5.0}, "square"}; // all specified
std::cout << "=== Nested Aggregates ===" << std::endl;
print_rect(r1);
print_rect(r2);
print_rect(r3);
std::cout << std::endl;
// ------------------------------------------------------------------
// 5. Array member defaults
// ------------------------------------------------------------------
Sensor s1{}; // all defaults
Sensor s2{42, {1.5, 2.5, 3.5}}; // active defaults to true
Sensor s3{99, {0.0, 0.0, 0.0}, false}; // fully specified
std::cout << "=== Array Member Defaults ===" << std::endl;
std::cout << " s1: id=" << s1.id << " active=" << s1.active
<< " readings=[" << s1.readings[0] << "," << s1.readings[1]
<< "," << s1.readings[2] << "]" << std::endl;
std::cout << " s2: id=" << s2.id << " active=" << s2.active
<< " readings=[" << s2.readings[0] << "," << s2.readings[1]
<< "," << s2.readings[2] << "]" << std::endl;
std::cout << " s3: id=" << s3.id << " active=" << s3.active
<< " readings=[" << s3.readings[0] << "," << s3.readings[1]
<< "," << s3.readings[2] << "]" << std::endl;
return 0;
}
/*
Expected output:
=== All Defaults ===
host=localhost port=8080 retries=3 ssl=false
=== Partial Override ===
host=api.example.com port=443 retries=3 ssl=false
=== Full Override ===
host=db.internal port=5432 retries=5 ssl=true
=== Nested Aggregates ===
[unnamed] (0,0) -> (0,0)
[unnamed] (1,2) -> (10,20)
[square] (0,0) -> (5,5)
=== Array Member Defaults ===
s1: id=-1 active=true readings=[0,0,0]
s2: id=42 active=true readings=[1.5,2.5,3.5]
s3: id=99 active=false readings=[0,0,0]
*/
Run command
g++ -std=c++14 -Wall -Wextra -o example aggregate_member_init.cpp