List, Aggregate, and Designated Initialization

Aggregate Initialization with Inheritance

How does aggregate initialization interact with inheritance and derived classes?

Abstract art representing computer programming

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.

This Question is from the Lesson:

List, Aggregate, and Designated Initialization

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

Answers to questions are automatically generated and may not have been reviewed.

This Question is from the Lesson:

List, Aggregate, and Designated Initialization

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

A computer programmer
Part of the course:

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Free, unlimited access

This course includes:

  • 125 Lessons
  • 550+ Code Samples
  • 96% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Free, Unlimited Access

Professional C++

Comprehensive course covering advanced concepts, and how to use them on large-scale projects.

Screenshot from Warhammer: Total War
Screenshot from Tomb Raider
Screenshot from Jedi: Fallen Order
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved