Jump to content
  • Advertisement
Sign in to follow this  
Ryan_001

C++ virtual inheritance

This topic is 1001 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

A simple question regarding virtual inheritance in C++. Given the following inheritance hierarchy:

struct A {
	A();	// make calling this a compile time error
	A(int) {}
	};

struct B : virtual public A {};

struct C : virtual public A, virtual public B {
	C() : A(5) {}
	};

Is there any way to make calling A::A() a compile time error?  For example:

A a;     // error
B b;     // error
C c;     // valid 

Obviously I can throw a run-time error, but I'd prefer a compile time error.

Edited by Ryan_001

Share this post


Link to post
Share on other sites
Advertisement

Thank-you for the responses, but its clear I was not clear.  What I want to prevent is this:

struct D : virtual public A, virtual public B {
   D(){}
   };

D d;   // error here

The base class default constructor is only there to keep the other abstract classes placated, it should not ever actually be called. In this example A::A() should never be called. Now normally leaving the function declared but undefined (or more recently use = delete) is a fine solution, the program will compile fine and only fail to compile if the function is actually called. Problem is in this case, even though A::A() is never be called, its still required due to the compiler generated B::B().  Am I making any sense?  Its probably too early to be posting about this...

Share this post


Link to post
Share on other sites

The base class default constructor is only there to keep the other abstract classes placated, it should not ever actually be called. In this example A::A() should never be called. Now normally leaving the function declared but undefined (or more recently use = delete) is a fine solution, the program will compile fine and only fail to compile if the function is actually called. Problem is in this case, even though A::A() is never be called, its still required due to the compiler generated B::B(). Am I making any sense? Its probably too early to be posting about this...

 

So what you want is that A cannot be directly instantiated, while D can be?

A a; // should error
D d; // should compile

Because your mentioning of "A::A" should never be called is a little confusing. If you construct an object that inherits from A, the A ctor will have to be called. If you just don't want anyone to be able to instantiate A directly, hodgmans suggestion of setting it protected should do it:

class A
{
protected:
    A() {} // now only classes derived from A can call this
}

class D : virtual public A
{
}

A a; // this fails... 
D d; // ... but this compiles

Otherwise you need to clearly elaborate better what you want to do.

Edited by Juliean

Share this post


Link to post
Share on other sites

When using virtual inheritance, ie. in the hierarchy above, you only have one instance of the virtual class in the resulting object.  So there is only one A, but it must be constructed in every derived class that inherits it (classes B, C, and D).  Now if A (and B, C, and D by extension) all have default constructors, everything works cleanly.  But say A doesn't have a default constructor, then B would have to (in its constructor) create A.  So you pass the parameters to construct A to B along with any additional parameters, despite these parameters never being used because you have no intention of ever creating B, or creating A through B.  As the hierarchy gets even slightly complex this deep parameter passing becomes a problem.

 

The easiest solution (apart from don't use MI) is to ensure all abstract base classes have a protected default constructor.  This prevents them from accidentally being constructed, but they can easily be virtually inherited, and the final class then can call the appropriate non-default constructors for each virtual 'instance' as required.  The only thing is sometimes you forget to call the appropriate non-default constructors for a base class.  Since these default base class constructors aren't supposed to be default constructed, this would yield an invalid object.  The solution I've always used was to simply throw an exception from the base class default constructors that aren't to be called and document appropriately.  I was wondering if there was some way to turn that run-time error into a compile time one.

Share this post


Link to post
Share on other sites

Trying to understand what you mean here..

 

You don't want to be able to instantiate class A using a default constructor but you also don't want the child classes to be able call the default constructor and so protected won't work?

 

If that is the case, then don't define a default constructor in class A and just have your non-default constructors protected instead.

Share this post


Link to post
Share on other sites

struct C : virtual public A, virtual public B {
C() : A(5) {}
};

If your virtual base class stores values like that, you are almost certainly picking the wrong pattern for the job.

 

Virtual inheritance is really useful in a small number of cases. Except for those very few times, it is the wrong solution. Generally the only time it is useful is base classes that are purely utilitarian, mandatory within a wide number of classes, yet useless unless part of another class's functionality; the base must provide the functionality to a tree that requires multiple inheritance, yet must only have a single form of the utility it provides. It is an extremely uncommon requirement.

 

One of those very rare cases is the C++ ios_base class, inherited with virtual inheritance.  It has IO flags constants (decimal, octal, hexadecimal, left-justified, right-justified, etc), locale information, and stream constants (good, bad, fail, eof).   Note how these are pure utilities, they exist in all types of streams, and are otherwise useless without an actual stream.  No matter the type of stream (input-only, output-only, input-and-output) it still must only have a single instance of these utilities. Having more than one instance of the utilities would be nonsensical; you cannot logically have two states one with "fail" the other with "EOF", or two sets of flags indicating both decimal and hexadecimal formats.  In this very rare case where the design requires multiple inheritance and also shared behavior with a single base, virtual inheritance is needed.

 

Out of the tens of thousands of classes I've worked with over the years, the number of times virtual inheritance has been the right solution is something I can count on my fingers.  It is a very rarely used solution, but in those rare cases it is important to have.

 

 

 

In your case where you demonstrate feeding a virtual parent class with a parameter, it shows you are not in the very rare situation where virtual inheritance is the right solution.

 

Since your class is specifying a parameter to the base, that means another class could also specify that type of parameter to the base, with the result that you are constructing the single base more than once. Attempting to construct it more than once won't do what you want for all but one of the places where it happens.  

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!