Damian Reiter
Damian Reiter

Reputation: 35

C++ initializer list members still calling default constructor?

I'm having a problem initializing a shared_ptr member of a class I'm working on.

I have two classes, A and B:

class A {
  int _x;
  int _y;

  public:
    A(int, int);
};

A::A(int x, int y) {
  _x = x;
  _y = y;
}

class B : public A {
  std::shared_ptr<A> _a;

  public:
    B(std::shared_ptr<A>);
};

B::B(std::shared_ptr<A> a) : _a(a) {}

int main() {
  std::shared_ptr<A> a = std::make_shared<A>(1, 2);

  B b(a);

  return 0;
}

I just want class B to hold a std::shared_ptr to an instance of class A. However, I'm getting the error no matching function for call to A::A() in B's constructor.

I'm confused because I thought the point of the initializer list is to avoid implicitly calling the default constructor for member variables, but it still seems to be trying to call A's default constructor.

Any explanation is appreciated, thanks.

edit: after more messing around, it seems like it complies properly if B does not inherit from A. Still unsure why inheriting from A results in A's default constructor being called from B's constructor.

Upvotes: 2

Views: 801

Answers (2)

Grant Schulte
Grant Schulte

Reputation: 153

When you call B b(a);, you construct a B object using the constructor B::B(std::shared_ptr<A> a).

The issue is in your definition of this constructor. B inherits from A, meaning that B is not just a B, but instead is really an A and B stacked on top of each other. A is the base class, so it is on the bottom. So when you try to construct a B object, the code must first construct the A object at the bottom/base to form the foundation for the B object to exist on. That means that you actually have to tell the code how to construct the A object.

Because you defined a constructor for A that takes two int variables, the compiler does not generate a default constructor for A. Therefore, you must explicitly write out how A should be constructed within B. Constructing the A object can be done in the initializer list of the B object's constructor:

B::B(std::shared_ptr<A> a) : A(0,0), _a(a) {}

Upvotes: 2

David Schwartz
David Schwartz

Reputation: 182763

Since B is derived from A, every B is an A. That means to construct a B, you must also construct an A. However, you do not tell B's constructor how to construct an A, so the default constructor is used.

If you want B to be derived from A and only hold a shared_ptr to A, then A must not do anything you don't want B to do. Here, A has _x and _y.

It's not clear what you really want, but perhaps you want some other base class that both A and B derive from that only has what both A and B should have.

The classic example of inheritance in C++ is something like Instrument being the base class that has members like Play with derived classes like Trumpet and Clarinet having the thing that makes some particular instrument a trumpet. Only what is common to Trumpets and Clarinets should be in Instrument.

Upvotes: 2

Related Questions