Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ private virtual inheritance problem

Tags:

In the following code, it seems class C does not have access to A's constructor, which is required because of the virtual inheritance. Yet, the code still compiles and runs. Why does it work?

class A {};
class B: private virtual A {};
class C: public B {};

int main() {
    C c;
    return 0;
}

Moreover, if I remove the default constructor from A, e.g.

class A {
public:
    A(int) {}
};
class B: private virtual A {
public:
    B() : A(3) {}
};

then

class C: public B {};

would (unexpectedly) compile, but

class C: public B {
public:
    C() {}
};

would not compile, as expected.

Code compiled with "g++ (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125)", but it has been verified to behave the same with other compilers as well.

like image 573
Oak Avatar asked Mar 03 '10 12:03

Oak


People also ask

How do I get rid of Diamond problems?

The solution to the diamond problem is to use the virtual keyword. We make the two parent classes (who inherit from the same grandparent class) into virtual classes in order to avoid two copies of the grandparent class in the child class.

What is Virtual inheritance give example?

There are two vtable pointers, one per inheritance hierarchy that virtually inherits Animal . In this example, one for Mammal and one for WingedAnimal . The object size has therefore increased by two pointers, but now there is only one Animal and no ambiguity.

What is the effect of using private inheritance?

The private inheritance allows members of Car to convert a Car* to an Engine*. The private inheritance allows access to the protected members of the base class. The private inheritance allows Car to override Engine's virtual functions.

How does Virtual inheritance solve the diamond problem?

Virtual inheritance solves the classic “Diamond Problem”. It ensures that the child class gets only a single instance of the common base class. In other words, the Snake class will have only one instance of the LivingThing class. The Animal and Reptile classes share this instance.


2 Answers

According to C++ Core Issue #7 class with a virtual private base can't be derived from. This is a bug in compiler.

like image 123
Kirill V. Lyadvinsky Avatar answered Oct 21 '22 11:10

Kirill V. Lyadvinsky


For the second question, it is probably because you don't cause it to be implicitly defined. If the constructor is merely implicitly declared, there is no error. Example:

struct A { A(int); };
struct B : A { };
// goes fine up to here

// not anymore: default constructor now is implicitly defined 
// (because it's used)
B b;

For your first question - it depends on what name the compiler uses. I have no idea what the standard specifies, but this code for instance is correct because the outer class name (instead of the inherited class name) is accessible:

class A {};
class B: private virtual A {};
class C: public B { C(): ::A() { } }; // don't use B::A

Maybe the Standard is underspecified at this point. We'll have to look.


There does not seem to be any problem with the code. Furthermore, there is indication that the code is valid. The (virtual) base class subobject is default initialized - there is no text that implies that name lookup for the class name is dine inside the scope of C. Here is what the Standard says:

12.6.2/8 (C++0x)

If a given non-static data member or base class is not named by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class

[...] otherwise, the entity is default-initialized

And C++03 has similar text (thou less clear text - it simply says its default constructor is called at one place, and at another it makes it dependent on whether the class is a POD). For the compiler to default initialize the subobject, it just has to call its default constructor - there is no need to lookup the name of the base class first (it already knows what base is considered).

Consider this code that certainly is intended to be valid, but that would fail if this would be done (see 12.6.2/4 in C++0x)

struct A { };
struct B : virtual A { };
struct C : B, A { };
C c;

If the compiler's default constructor would simply look-up class name A inside of C, it would have an ambiguous lookup result with regard to what subobject to initialize, because both the non-virtual A and the virtual A's class-names are found. If your code is intended to be ill-formed, i would say the Standard certainly needs to be clarified.


For the constructor, notice what 12.4/6 says about the destructor of C:

All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes.

This can be interpreted in two ways:

  • calling A::~A()
  • calling ::A::~A()

It seems to me that the Standard is less clear here. The second way would make it valid (by 3.4.3/6, C++0x, because both class-names A are looked up in global scope), while the first will make it invalid (because both A will find the inherited class names). It also depends what subobject the search starts with (and i believe we will have to use the virtual base class' subobject as start point). If this goes like

virtual_base -> A::~A();

Then we will directly find the virtual base' class name as a public name, because we won't have to go through the derived class' scopes and find the name as non-accessible. Again, the reasoning is similar. Consider:

struct A { };
struct B : A { };
struct C : B, A {
} c;

If the destructor would simply call this->A::~A(), this call would not be valid because of the ambiguous lookup result of A as an inherited class name (you cannot refer to any non-static member-function of the direct base class object from the scope C, see 10.1/3, C++03). It will uniquely have to identify the class names that are involved, and has to start with the class' subobject reference like a_subobject->::A::~A();.

like image 38
Johannes Schaub - litb Avatar answered Oct 21 '22 10:10

Johannes Schaub - litb