why are virtual base non-default constructors not called unless most-derived base explicitly invokes them?

Related searches

I would like to understand WHY C++ standard mandates that virtual base non-default constructors cannot be invoked by an intermediate NOT most-derived class, as in this code, when compiled with '-D_WITH_BUG_' :

/*  A virtual base's non-default constructor is NOT called UNLESS 
 *  the MOST DERIVED class explicitly invokes it
 */

#include <type_traits>
#include <string>
#include <iostream>

class A
{
public:
    int _a;
    A():  _a(1)
    {
        std::cerr << "A() - me: " << ((void*)this) << std::endl;
    }
    A(int a): _a(a)
    {
        std::cerr << "A(a) - me:" << ((void*)this) << std::endl;
    }
    virtual ~A()
    {
        std::cerr << "~A" << ((void*)this) << std::endl;
    }
};

class B: public virtual A
{
public:
    int _b;
    B(): A(), _b(2)
    {
        std::cerr << "B() - me: " << ((void*)this) << std::endl;
    }
    B(int b) : A(), _b(b)
    {
        std::cerr << "B(b) - me: " << ((void*)this) << std::endl;
    }
    B(int a, int b): A(a), _b(b)
    {
        std::cerr << "B(a,b) - me: " << ((void*)this) << std::endl;
    }
    virtual ~B()
    {
        std::cerr << "~B" << ((void*)this) << std::endl;
    }
};

class C: public virtual B
{
public:
    int _c;
    C(): B(), _c(3)
    {
        std::cerr  << "C()" << std::endl;
    }
    C(int a, int b, int c)
    :
#ifdef _WITH_BUG_    
    B(a,b)
#else
    A(a), B(b)
#endif    
    , _c(c)
    {
        std::cerr  << "C(a,b) - me: " << ((void*)this) << std::endl;    
    }
    virtual ~C()
    {
        std::cerr << "~C" << ((void*)this) << std::endl;
    }  
};
extern "C"
int main(int argc, const char *const* argv, const char *const* envp)
{
    C c(4,5,6);
    std::cerr << " a: " << c._a  << " b: " << c._b << " c: " << c._c 
              <<  std::endl;
    return 0;
}

So, when compiled WITHOUT -D_WITH_BUG_, the code prints:

$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \
  -Wno-unused -fno-pretty-templates -Wno-register  \
  tCXX_VB.C -o tCXX_VB 
$ ./tCXX_VB
A(a) - me:0x7ffc410b8c10
B(b) - me: 0x7ffc410b8c00
C(a,b) - me: 0x7ffc410b8bf0
a: 4 b: 5 c: 6
~C0x7ffc410b8bf0
~B0x7ffc410b8c00
~A0x7ffc410b8c10

But when compiled with -D_WITH_BUG_ :

$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \ 
  -Wno-unused -fno-pretty-templates -Wno-register \
  -D_WITH_BUG_ tCXX_VB.C -o tCXX_VB
$ ./tCXX_VB
A() - me: 0x7ffd7153cb60
B(a,b) - me: 0x7ffd7153cb50
C(a,b) - me: 0x7ffd7153cb40
a: 1 b: 5 c: 6
~C0x7ffd7153cb40
~B0x7ffd7153cb50
~A0x7ffd7153cb60

Why must B(int a, int b)'s invocation of A(a) be ignored here ? I understand the C++ standard mandates it, but why ? What is the rational ?

If I instantiate just a B object : B b(4,5) ; this DOES get a correct b._a value of 4; but if B is a subclass of C: C c(4,5,6) C::a ends up being 1, IFF c does not DIRECTLY INVOKE A(a) . So the value of a B(a,b) is different if it is a subclass object than if it is a most-derived object . This to me is very confusing and wrong. Is there any hope of getting enough people to agree to change the C++ standard on this ?

The entire purpose of having virtual inheritance is to solve the diamond problem. Once you have a virtual base class, and your hierarchy looks like this:

  A
 / \
B   C
 \ /
  D

You need to know when to construct the A. You can't have B construct it and then C then immediately overwrite it - you need it to be constructed exactly once. Okay, so when can we do that? The simplest choice is just: make the most derived class do it! So when we're initializing the B subobject of D, it will not initialize its A subobject because B is not the most derived type.

In your case, your hierarchy is still linear:

A
|
B
|
C

but the most derived type, C, has to initialize all the virtual bases - A and B. B won't initialize its A subobject for the same reason as it didn't in the complicated example.

Inheritance — What your mother never told you, C++ , With derived classes, things are slightly more complex: If no base constructor is specified, the default constructor will be used. While this actually works in this case, it wouldn't work if m_id were a const or a reference Fortunately, C++ gives us the ability to explicitly choose which Base class constructor will be called ! Every constructor is setting the pointer to its own vtable. If it uses the virtual mechanism for function calls, it will produce only a call through its own vtable, not the most-derived vtable. In summary, unlike destructor, constructor works downward in the class hierarchy: base class to more derived class. Each vtable is for that class only.

It's unlikely that you'll get any support to change the language. Virtual inheritance is only useful in multiple inheritance scenarios.

Why must B(int a, int b)'s invocation of A(a) be ignored here?

Because the unique A sub-object has already been constructed. A constructor isn't an ordinary function, you can't just call it anywhere.

You can write

C(int a, int b, int c)
    : A(a), B(a, b), _c(c)
    { ... }

which will give the body of B::B(int, int) the parameter that passed to A::A(int)

11.4 — Constructors and initialization of derived classes, You can define as many overloaded constructors as needed to If we didn't zero -init here, default constructor would // leave them of a class, the compiler chooses which constructor to invoke based on the rules of overload resolution: constexpr functions in general;; the class has no virtual base classes;� In C++11, a form of 'constructor inheritance' has been introduced where you can instruct the compiler to generate a set of constructors for you that take the same arguments as the constructors from the base class and that just forward those arguments to the base class.

This behavior is because of virtual base class. Since A is the virtual base class it is constructed by the most derived class. you can check about diamond shape inheritance problem and this discussion on similar question to understand why it has to be in this way. First understand how diamod shape problem is solved by virtual base class. class A { ...} class B: virtual public A {...} class C: virtual public A {...} class D: public B, public C {...} When you make the base class as virtual there will be one base class object. The intermediate derived class objects will all refer to the same single base class object. I.e. here if an object of D is created then B::A and C::A both will refer the same object. This single object is of base class of both B and C. So there are two derived classes to construct this single object if it allowed the construction of base class object by intermediate classes. This ambiguity is solved by giving the most derived class the responsibility to construct the virtual base class.

Constructors (C++), The C++ Standard, [class.cdtor], paragraph 4 [ISO/IEC 14882-2014], states the If the virtual function call uses an explicit class member access and the object to more derived classes, attempting to call a derived class function from a base that involve calling (non-pure) virtual functions from the constructor of a class, it is � Hi Waqar, Constructor can't be overridden, as they're not inherited. However, I believe it's basically saying that unless you want to call the parameterless constructor of the base type (in which case, making that fact explicit isn't a bad idea if your constructor takes any arguments) you have to call a different constructor of the base type.

OOP50-CPP. Do not invoke virtual functions from constructors or , Even if this class manages no resources, it is a good idea to define a virtual destructor for every base class. This allows derived constructors to be called if the base class is used in a Note that a class may inherit from more than one base class. The default inheritance model in C++ is private inheritance for classes and� Classes in Java inherit constructors from their parent classes. If you don't explicitly define a parent class, then Object is used, which has only the default empty constructor. That "default

8.4. Inheritance — Intermediate Modern C++ Overview, The C++11 standard says, > A destructor is used to destroy objects of its class type. A destructor destroys an object; that much is certain. What else the destructor does is up to you.

However, it's possible to still do it using a trick. When using virtual inheritance, the initialization list of the most-derived-class's constructor directly invokes the virtual base class's constructor. This means that if we can hide access to the virtual base class's constructor, then we can prevent any class from deriving from it.

Comments
  • Can you format your code a bit better? the way it is currently written is just a mess.
  • "virtual base non-default constructors " constructors cannot be virtual though.
  • @Rakete1111 I believe it should be read as virtual base non-default constructors :)
  • This isn't the problem, but names that begin with an underscore followed by a capital letter (_WITH_BUG_) and names that contain two consecutive underscores are reserved for use by the implementation. Don't use them in your code.
  • The issue here isn't non-default constructors; the rule is that virtual bases are initialized by the constructor for the most-derived type. That applies to default constructors as well as non-default constructors.
  • to me that is kludgey, that the programmer must remember that their explicit invocation of B(a,b) is ignored.
  • The other option is to not virtually inherit
  • correction, that does not really explain the rationale, only that the C++ standard mandates it to be the case. IMHO, once a programmer has declared that B 'is an A', virtually or not, then B should be responsible for constructing its 'A' .
  • I still see no reason why B cannot always construct its A, regardless of whether or not it is a virtual base of some class C . But I must accept this is the way it is ...
  • "its 'A'" -> shared with all the other classes virtually inheriting A in a deriving class
  • @JVD Here B cannot construct 'A' since A is a virtual base the most derived class C has to construct it. This is how the diamond problem is solved. you can check the post link stackoverflow.com/questions/2659116/… . If you see there if the intermediate classes constructs the virtual base then all intermediate classes will need to construct the single virtual base class object.