3

i have the following scenario:

class A
{
  public:
    A(std::string id);
};

class B : public virtual A
{
  public:
    B();
};

class C : public virtual A
{
  public:
    C();
};

class D : public B, public C
{
  public:
    D(std::string id);
};


D::D(std::string id) : A(id), B(), C()
{
}


class X : public D
{
  public:
    X(std::string id);
}

X::X(std::string id) : D(id)
{
}

Now, if i create an instance of D everything works fine. However if i create an instance of X i get a compiler error which tells me that something tries to call the default constructor of A - which does not exist. If i create it, it compiles but only the default constructor is called and accordingly, id is not correctly set/initialized.

This can be fixed by implementing the constructor of X like so:

X::X(std::string id) : A(id), D(id)
{
}

But my understanding is, that this should be unnecessary. So where's my error ?

joekr
  • 1,543
  • 1
  • 16
  • 22
  • `X::X` must initialize `A`, because `D::D` when used as a base class does not initialize `A` (the `A(id)` initializer is ignored). – aschepler Jan 29 '13 at 13:50

1 Answers1

2

You need to make all your constructors public and define a default constructor for A because the string constructor will mark the default constructor as =delete. Furthermore, the most derived class will initialize any virtual base class, quoting from the draft Standard:

12.6.2 Initializing bases and members [class.base.init]

10 In a non-delegating constructor, initialization proceeds in the following order: — First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

In this case that means that X must indeed initalize A.

#include <iostream>
#include <string>

class A
{
public:
  A() { std::cout << "A\n"; }
  A(std::string id) { std::cout << id << " A(id)\n"; }
};

class B : public virtual A
{
public:
   B() { std::cout << "B\n"; }
};

class C : public virtual A
{
public:
   C() { std::cout << "C\n"; }
};

class D : public B, public C
{
public:  
   D(std::string id): A(id) { std::cout << id << " D(id)\n"; }
};


class X : public D
{
public:
  X(std::string id): A(id), D(id) { std::cout << id << " X(id)\n"; }
};

int main()
{
   X x("bla");
   x;       
}
Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304
  • All my constructor are public, but adding the default constructor in A causes A() instead of A(id) to be called when creating an instance of X. – joekr Jan 29 '13 at 13:24
  • In that case my question would be: is there any way around that ? As A,B,C and D make up a library and i don't want users of the library to know anything about A. They are only supposed to know and use D. – joekr Jan 29 '13 at 14:20
  • @joekr AFAIK, there is no way around it except for refactoring your class hierarchy. Note that this is one of the reasons why composition is almost always favored over inheritance, especially over inheritance of classes mainting state. – TemplateRex Jan 29 '13 at 14:21
  • One option is to store the id in `D` and add a virtual function for retrieving it. – Bo Persson Jan 29 '13 at 15:16