Why is repeated inheritance / duplicate inheritance invalid?

c++, inheritance, language-lawyer, multiple-inheritance

Solution

Why isn't this supported by C++ ?

The reason why your compiler doesn't accept this kind of multiple inheritance, is because it's explicitely forbidden in the C++ standard:

Section 10.1 point 3: A class shall not be specified as a direct base class of a derived class more than once. [ Note: A class can be an indirect base class more than once and can be a direct and an indirect base class.(...) ]

So regardless of the philosophical justification of such an inheritance, this is invalid C++ :

struct DoubleAgent : public Agent, public Agent {};  // direct base class more than once !!!

The reason is purely syntactical. Supose you'd have the following definition of agent :

struct Agent {  double salary; }; 

Would the double inheritance be valid, you wouldn't have any way do disambiguate which of the two inherited salary you want to access.

How shall this be correctly done in C++ ?

The (existing) standard solution is to use an empty class to disambiguate the multiple role that your base class would have:

struct AgentFromSouth : public Agent {}; 
struct AgentFromNorth : public Agent {}; 
struct DoubleAgent : public AgentFromSouth, public AgentFromNorth {};  // valid

Diambiguation is done as follows:

DoubleAgent spy; 
spy.AgentFromSouth::salary = 10000.0; 
spy.AgentFromNorth::salary = 800.0;

This is not a workaround ! The intermediary classes are not a simple workaround. They offer a consistent subtyping semantic:

- If a `DoubleAgent`inherits twice from the same class, this same class has de facto two distinct roles. You'd like to keep these roles anonymous, but the standard forces you to give them a name. It contributes hence to a better understanding of the code by human readers.

- If a `DoubleAgent` inherits from (i.e. "is a") `AgentFromNorth`, then indepenently of the multiple inheritance, I should be allowed to write: `AgentFromNorth *p = &spy;` Any alternative syntax that you'd propose would need to comply to this principle.

Technically, with a modern compiler such an empty class doesn't generate significant code overhead either (the assembly code generated by my compiler for inheriting via an empty class show is exactly the same as with direct inheritance.)

Further discussion about alternative approaches

The template based solution that you propose is certainly an idea. But both proposed alternatives are just syntactic sugar to give two different names ( `Agent<1>` or `Primary<Agent>`) for the same base class. It's exactly what is achieved by creating an intermediate empty "alias" class, with the standard of today. But with more problems.

In fact such a syntax would cause a dependency issue. While for all classes, forward declaration rules, and class visibility are clearly defined for existing construct, this would not be completely the case with your alternative approach.

For example, to define a pointer to a base class, I wouldn't be able to write:

DoubleAgent *pspy = ....; 
Agent *p = pspy;  //  ambiguous:  which role should I use ?  Agent<1> or Agent<2>  ? 

Now you would certainly say that I could very well write something like:

Agent<1> *p = pspy;   

But the meaning of `Agent<1>` is only relevant in the context of `DoubleAgent`. It's not a type with it's own independent dfinition. And how would you interpret:

class QuadrupleAgent : DoubleAgent, DoubleAgent {...};  
QuadrupleAgend *pmasterspy;
Agent<1> *p = pmasterspy; // Ambiguous: is it DoubleAgent<1>::Agent<1> or DoubleAgent<2>::Agent<1> ? 

All these issues are perfectly well handled and in a consistent manner by the standard. Your short-circuit syntax would raise more questions than it solves. What would then be the benefit of creating such new and cumbersome language constructs to provide alternative for existing simple constructs ?

Problem

This code is invalid: ``` struct Agent {}; struct DoubleAgent : public Agent, public Agent {}; ``` Because: ``` > g++ -c DoubleAgent.cpp DoubleAgent.cpp:2:43: error: duplicate base type ‘Agent’ invalid struct DoubleAgent : public Agent, public Agent {}; ^ ``` Why? I don't think this violates the concept of inheritance; if a class can have an is-a relationship with a base class, then shouldn't it be able to have an is-two-of-a relationship with a base class? Especially considering that, since C++ supports multiple inheritance, a class can already be multiple different base types. Furthermore, this is already sort of supported indirectly by the so-called diamond problem, where multiple (distinct) base classes can inherit from a common base class. I found a couple of quotes from Wikipedia that seem to imply that this is a common but not universal constraint in OO languages, may only be a result of fears of syntactic complexity, and is an undesirable constraint: Wikipedia: Inheritance (object-oriented programming)#Design constraints Design constraints Using inheritance extensively in designing a program imposes certain constraints. For example, consider a class Person that contains a person's name, date of birth, address and phone number. We can define a subclass of Person called Student that contains the person's grade point average and classes taken, and another subclass of Person called Employee that contains the person's job-title, employer, and salary. In defining this inheritance hierarchy we have already defined certain restrictions, not all of which are desirable: - Singleness: using single inheritance, a subclass can inherit from only one superclass. Continuing the example given above, Person can be either a Student or an Employee, but not both. Using multiple inheritance partially solves this problem, as one can then define a StudentEmployee class that inherits from both Student and Employee. However, in most implementations, it can still inherit from each superclass only once, and thus, does not support cases in which a student has two jobs or attends two institutions. The inheritance model available in Eiffel makes this possible through support for repeated inheritance. Wikipedia: Multiple Inheritance#Mitigation C++ does not support explicit repeated inheritance since there would be no way to qualify which superclass to use (i.e. having a class appear more than once in a single derivation list [class Dog : public Animal, Animal]). I disagree with that statement "... since there would be no way to qualify which superclass to use". Couldn't the compiler just allow some kind of indexing of the base qualifier, e.g. `Animal[1]::method()` (and, in the same vein, perhaps allow collapsing the repetition in the class definition to `struct DoubleAgent : public Agent[2] {};`)? I don't see any problem with that, and isn't it quite a simple solution, syntactically and conceptually? I believe I've found a couple of possible workarounds, although I can't say for certain whether they would actually work in a real system, or whether they would cause insurmountable problems: 1: Disambiguating template non-type parameter on the base class. ``` template<int N> struct Agent {}; struct DoubleAgent : public Agent<1>, public Agent<2> {}; ``` 2: Disambiguating intermediate base classes (diamond inheritance). (Note: I didn't have to use template classes with CRTP here, but I think that would aid in generality and reduction of duplicate code, if this pattern (anti-pattern?) were to be used repeatedly.) ``` template<typename T> struct Primary : public T {}; template<typename T> struct Secondary : public T {}; struct Agent {}; struct DoubleAgent : public Primary<Agent>, public Secondary<Agent> {}; ``` Please comment on: - the general concept of inheritance / multiple inheritance / repeated inheritance, - the rationale behind non-support for repeated inheritance in C++ (or other languages), - the rationale behind the apparent support for repeated inheritance in Eiffel (or other languages), and - my possible workarounds shown above. Edit: I'd like to emphasize my suggested syntax for repeated inheritance in C++, since I feel that its simplicity and comprehensiveness are being overlooked or underappreciated by the answers. The following code demonstrates the idea, covering both casting (which was commented on by @stefan) and member qualification: ``` struct Agent { int id; }; struct TripleAgent : public Agent[3] {}; int main() { TripleAgent tripleAgent; tripleAgent.Agent[0]::id = 1; tripleAgent.Agent[1]::id = 2; tripleAgent.Agent[2]::id = 3; Agent* agent0 = (Agent[0]*)tripleAgent; Agent* agent1 = (Agent[1]*)tripleAgent; Agent* agent2 = (Agent[2]*)tripleAgent; } ``` As you can see, `Agent[N]` basically becomes a type name in its own right, but is only valid when qualifying or casting a type that possesses `N+1` or more instances of the underlying type. This adds very little complexity to the language, is very intuitive (in my opinion) since it mirrors array indexing which programmers are already familiar with, and I believe is all-encompassing (i.e. doesn't leave any ambiguities). The main difficulties answerers seem to be having with it are as follows: - It's complex, arbitrary, or outrageous. - There are alternative solutions that don't require language modification. - The required standard/compiler changes would involve too much effort/work. My replies: - No it's not; it's the simplest and most logical solution, in my opinion. - Those are workarounds, not solutions. You shouldn't have to create an empty class or have the compiler generate a redundant template variation to disambiguate the parent classes. And, for the template workaround, I believe this would involve redundancy in the generated binary, as the compiler would generate separate implementations of each variation (e.g. `Agent<1>` and `Agent<2>`), when that should not be necessary (please correct me if I'm wrong; is the compiler smart enough to know that the template parameter is not used in the class definition, so it won't generate separate code for each distinct argument to that parameter?). - I suppose this is the best argument; the usefulness of the feature may not justify the effort to make it happen. But this is not an argument against the idea itself. When I asked this question, I was hoping for a more theoretical, academic discussion of the idea, not a "we don't have time for this" reaction. Edit: Here's another possible syntax, which I actually like better. It allows a supertype name to be accessed as a member, which it kind of is: ``` struct Person { int height; }; struct Agent { int id; }; struct TripleAgent : public Person, public Agent[3] {}; int main() { TripleAgent tripleAgent; tripleAgent.Person.height = 42; tripleAgent.Agent[0].id = 1; tripleAgent.Agent[1].id = 2; tripleAgent.Agent[2].id = 3; Person& person = tripleAgent.Person; Agent& agent0 = tripleAgent.Agent[0]; Agent& agent1 = tripleAgent.Agent[1]; Agent& agent2 = tripleAgent.Agent[2]; } ```

Original source

Related problems