Complete Guide to C++ Aggregate Initialization

Complete Guide to C++ Aggregate Initialization

이 글의 핵심

C++ aggregates: brace-initialization for structs and arrays without user-defined constructors. POD, C++17 base classes, C++20 designated initializers.

What is Aggregate Initialization? Why is it important?

Problem Scenario: Do simple structs need constructors?

Problem: Writing constructors for simple structs that only hold data increases boilerplate code.

struct Point {
    int x;
    int y;
    
    // Do we need a constructor?
    Point(int x, int y) : x(x), y(y) {}
};

int main() {
    Point p(10, 20);
}

Solution: Aggregate types can be created easily with brace initialization without constructors.

struct Point {
    int x;
    int y;
    // No constructor needed
};

int main() {
    Point p = {10, 20};  // Simple!
}

Table of Contents

  1. Conditions for Aggregate Types
  2. Basic Initialization Syntax
  3. Changes in C++17/20
  4. Array Initialization
  5. Common Errors and Solutions
  6. Production Patterns
  7. Complete Example

1. Conditions for Aggregate Types

C++17 Aggregate Conditions

  • Arrays, or
  • Classes/structs:
    • No user-defined constructors (default/delete is OK)
    • No private/protected non-static members
    • No virtual functions
    • No virtual/private/protected base classes
// ✅ Aggregate
struct Point {
    int x;
    int y;
};

// ✅ Aggregate (C++17+, base classes are OK)
struct Base {
    int a;
};

struct Derived : Base {
    int b;
};

// ❌ Not Aggregate (constructor exists)
struct NotAgg1 {
    NotAgg1(int x) : value(x) {}
    int value;
};

// ❌ Not Aggregate (private member exists)
struct NotAgg2 {
private:
    int x;
public:
    int y;
};

// ❌ Not Aggregate (virtual function exists)
struct NotAgg3 {
    virtual void func() {}
    int x;
};

Type Checking

#include <type_traits>

struct Agg { int x, y; };
struct NotAgg { NotAgg(int x) : value(x) {} int value; };

int main() {
    static_assert(std::is_aggregate_v<Agg>);      // true
    static_assert(!std::is_aggregate_v<NotAgg>);  // true
}

2. Basic Initialization Syntax

Struct Initialization

struct Person {
    std::string name;
    int age;
    double height;
};

int main() {
    // Initialize all members
    Person p1 = {"Alice", 30, 165.5};
    
    // Initialize some members (others get default values)
    Person p2 = {"Bob", 25};  // height = 0.0
    
    // Empty initialization
    Person p3 = {};  // name = "", age = 0, height = 0.0
    
    // C++11 uniform initialization
    Person p4{"Charlie", 35, 180.0};
}

Nested Structs

struct Address {
    std::string city;
    int zipcode;
};

struct Employee {
    std::string name;
    int id;
    Address address;
};

int main() {
    Employee emp = {
        "Alice",
        100,
        {"Seoul", 12345}
    };
    
    std::cout << emp.address.city << '\n';  // Seoul
}

3. Changes in C++17/20

C++17: Base Classes Allowed

struct Base {
    int a;
};

struct Derived : Base {
    int b;
};

int main() {
    // C++17+: Base classes are allowed in Aggregates
    Derived d = {{10}, 20};  // a = 10, b = 20
}

C++20: Designated Initializers

struct Point {
    int x;
    int y;
    int z;
};

int main() {
    // C++20: Specify member names
    Point p = {
        .x = 10,
        .y = 20,
        .z = 30
    };
}

4. Array Initialization

1D Arrays

int arr1[5] = {1, 2, 3, 4, 5};
int arr2[5] = {1, 2};  // {1, 2, 0, 0, 0}
int arr3[] = {1, 2, 3};  // Size auto-detected (3)

2D Arrays

int matrix[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

// Flattened initialization
int matrix2[2][3] = {1, 2, 3, 4, 5, 6};

Struct Arrays

struct Point { int x, y; };

Point points[3] = {
    {0, 0},
    {10, 20},
    {30, 40}
};

// C++20 Designated Initializers
Point points2[2] = {
    {.x = 0, .y = 0},
    {.x = 10, .y = 20}
};

5. Common Errors and Solutions

Error 1: Classes with Constructors

Symptom: error: no matching constructor.

// ❌ Constructor exists
struct NotAggregate {
    NotAggregate(int x) : value(x) {}
    int value;
};

// NotAggregate obj = {10};  // Error

// ✅ No constructor
struct Aggregate {
    int value;
};

Aggregate obj = {10};  // OK

Error 2: Private Members

// ❌ Private member exists
struct NotAggregate {
private:
    int x;
public:
    int y;
};

// NotAggregate obj = {1, 2};  // Error

// ✅ All members are public
struct Aggregate {
    int x;
    int y;
};

Aggregate obj = {1, 2};  // OK

Error 3: Exceeding Member Count

struct Point {
    int x;
    int y;
};

int main() {
    Point p1 = {1, 2};     // OK
    // Point p2 = {1, 2, 3};  // Error: 2 members but 3 values provided
}

Error 4: Inheritance and Aggregates

struct Base {
    int a;
};

// ✅ Public inheritance
struct Derived1 : Base {
    int b;
};

Derived1 d1 = {{10}, 20};  // OK

// ❌ Private inheritance
struct Derived2 : private Base {
    int b;
};

// Derived2 d2 = {{10}, 20};  // Error

6. Production Patterns

Pattern 1: Configuration Structs

struct DatabaseConfig {
    std::string host = "localhost";
    int port = 5432;
    std::string database = "mydb";
    std::string user = "admin";
    std::string password;
    int connection_pool_size = 10;
};

DatabaseConfig prod_config = {
    .host = "prod.example.com",
    .port = 5432,
    .database = "production",
    .user = "app_user",
    .password = "secret",
    .connection_pool_size = 100
};

Pattern 2: Test Fixtures

struct TestData {
    int input;
    int expected;
    std::string description;
};

std::vector<TestData> test_cases = {
    {0, 0, "zero"},
    {1, 1, "one"},
    {5, 25, "five squared"},
    {-3, 9, "negative squared"}
};

for (const auto& test : test_cases) {
    int result = square(test.input);
    assert(result == test.expected);
}

Summary

ConceptDescription
AggregateSimple types without constructors
InitializationUse braces {}
ConditionsNo constructors, public members, no virtual functions
C++17Base classes allowed
C++20Designated Initializers added

Aggregate Initialization is a fundamental feature of C++ for initializing simple data structures without boilerplate.


FAQ

Q1: What is an Aggregate?

A: A simple type without constructors, with all members being public. Examples include arrays and structs.

Q2: How is it different from POD?

A: POD (Plain Old Data) is an Aggregate type that also meets trivial conditions. Aggregate is a broader concept.

Q3: What happens if only some members are initialized?

A: Remaining members are initialized to default values or 0.

Q4: What changed in C++17?

A: Starting from C++17, base classes can be part of Aggregate types.

Q5: What changed in C++20?

A: Designated Initializers were introduced, allowing initialization by specifying member names.

Q6: Where can I learn more about Aggregates?

A:

One-liner summary: Aggregate Initialization allows you to initialize simple structs without boilerplate. Next, check out Initialization Order for more insights.