Aggregate Initialization with Inheritance

How does aggregate initialization interact with inheritance and derived classes?

Aggregate initialization in C++ has specific rules when it comes to inheritance. Generally, aggregate initialization cannot be used directly with derived classes if they have base classes that are not aggregates themselves. Let's delve into this with an example.

First, consider a base class and a derived class:

#include <iostream>

struct Base {
  int a;
};

struct Derived : public Base {
  int b;
};

int main() {
  Derived d{1, 2};  
  std::cout << "a=" << d.a
    << ", b=" << d.b;
}
a=1, b=2

In this example, Base is an aggregate because it has no user-defined constructors or private members. Consequently, Derived can also use aggregate initialization, inheriting the properties of Base.

Non-Aggregate Base Types

If Base has private members or user-defined constructors, Derived cannot use aggregate initialization directly:

#include <iostream>

struct Base {
  int a;

  Base() {};  
};

struct Derived : public Base {
  int b;
};

int main() {
  Derived d{1, 2};  
}
error: 'initializing': cannot convert from 'int' to 'Base'

This code will not compile because Base now has a user-defined constructor, making it non-aggregate. Therefore, Derived also becomes non-aggregate, and aggregate initialization cannot be used.

To properly initialize such classes, you must use constructors:

#include <iostream>

struct Base {
  int a;

  Base(){};
  Base(int x) : a{x} {} 
};

struct Derived : public Base {
  Derived(int x, int y) : Base{x}, b{y} {}
  int b;
};

int main() {
  Derived d{1, 2};
  std::cout << "a=" << d.a
    << ", b=" << d.b;
}
a=1, b=2

In this code, Derived has a constructor that initializes the base class Base and its own member b. This is the correct approach when dealing with inheritance and aggregate initialization limitations.

Multiple Inheritance

Another point to consider is multiple inheritance:

#include <iostream>

struct Base1 {
  int a;
};

struct Base2 {
  int b;
};

struct Derived : public Base1, public Base2 {
  int c;
};

int main() {
  Derived d{{1}, {2}, 3};  
  std::cout << "a=" << d.a
    << ", b=" << d.b
    << ", c=" << d.c;
}
a=1, b=2, c=3

In multiple inheritance, you can still use aggregate initialization if all base classes and the derived class are aggregates. Each base class is initialized in the order of inheritance.

In summary, aggregate initialization works with inheritance as long as all involved classes are aggregates. When base classes have private members or user-defined constructors, you must use constructors for initialization in derived classes.

List, Aggregate, and Designated Initialization

A quick guide to creating objects using lists, including std::initializer_list, aggregate and designated initialization

Questions & Answers

Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.

List Initialization with Private Members
Can we use list initialization for classes with private or protected members?
Aggregate Initialization for Nested Structures
How does aggregate initialization work for complex types with nested structures?
Using Initializer Lists in Custom Containers
How can we use std::initializer_list to initialize custom container types?
Designated Initializers with Private Members
Can designated initializers be used with types that have private or protected members?
Heterogeneous Initializer Lists
Can std::initializer_list be used with heterogeneous data types in any way?
List Initialization with constexpr and constinit
How does list initialization interact with constexpr and constinit specifiers in C++20?
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant