-1

In Java, I can easily have a parallel track of interface inheritance, and classes that both inherit and implement the interfaces:

public class Main {
  interface I1 {
    void f1();
  };
  interface I2 extends I1 {
     void f2();
  };
  static class C1 implements I1 {
    public void f1() {}
  };
  static class C2 extends C1 implements I1 {
    public void f2()  {}
  };
  public static void main(String[] args) {
    C2 c2 = new C2();
  }
}

This works just fine. However, trying the same thing in C++:

class I1 {
public:
  virtual void f1() = 0;
};
class I2 : public I1 {
public:
  virtual void f2() = 0;
};
class C1 : public I1 {
public:
  void f1() override {}
};
class C2 : public I2, public C1 {
public:
  void f2() override {}
};
int main()
{
  C2* c2 = new C2;
}

fails with

cppscratch.cpp(20): error C2259: 'C2': cannot instantiate abstract class
cppscratch.cpp(14): message : see declaration of 'C2'
cppscratch.cpp(20): message : due to following members:
cppscratch.cpp(20): message : 'void I1::f1(void)': is abstract

on Visual C++ 16.10. Making all ABC inheritance virtual seems to help, but produces ominous warnings(once again, in Visual C++)

cppscratch.cpp(17,1): warning C4250: 'C2': inherits 'C1::C1::f1' via dominance
cppscratch.cpp(12): message : see declaration of 'C1::f1'

The answers to this post suggest that this is just fine and I can ignore the warning. Is it really acceptable do to this? No hidden "dread diamond" bugaboos waiting to bite me?

UPDATE: I've read several additional blog posts and SO questions, and there is considerable disagreement on whether virtual inheritance as a solution to the "dread diamond" is ever permissible. But some authors (Good and bad sides of virtual inheritance in C++) at least suggest that the one acceptable use is what he calls "mix ins" which are basically the same as Java interfaces. The one drawback seems to be the need to use dynamic_cast which doesn't seem especially awful. So unless someone comes up with a more definitive reference saying yay or nay, I'm going to run with that.

Wheezil
  • 3,157
  • 1
  • 23
  • 36
  • You have a typical *diamond inheritance pattern*, which is commonly solved with virtual inheritance. Now when you know the term for the pattern, it should be easy to find plenty of tutorials and examples on how to solve it. – Some programmer dude Jul 26 '21 at 20:49
  • Is `C2` supposed to implement `I2` (instead of `I1`)? (and then it should compile) – Alexis Wilke Jul 26 '21 at 20:49
  • Your compiler is doing you a disservice with that last warning. Unfortunately, too many people in the past have tried to write code that uses multiple inheritance without understanding how it works in C++, which is where we get the phrase "dread diamond"; the (often wrong) "solution" of using virtual inheritance for every multiply-inherited base class; and the wrong-headed warning that you might not be smart enough to understand the code that you've written. – Pete Becker Jul 26 '21 at 21:04
  • @PeteBecker : thanks, that is getting closer to what I'm really trying to answer. You are suggesting that "interfaces" or "mix ins" is indeed a perfectly acceptable use of diamond inheritance? I've searched for an authoritative answer but not really found one. – Wheezil Jul 26 '21 at 21:14
  • according to [this thread](https://stackoverflow.com/questions/469508/) the "dominance" warning is a compiler bug that they aren't fixing due to being low priority – M.M Jul 26 '21 at 21:17
  • Whether mixins are appropriate depends on a couple things. Do you _need_ to pass around objects through points to the interfaces, and do you _need_ dynamic dispatch? You can probably get by with mixins it but it might be unintuitive and require lots of re-designing if you're coming from a Java background – alter_igel Jul 26 '21 at 21:18
  • I don’t use the term “diamond inheritance” because it doesn’t have a well-defined meaning. If you want to imitate Java interfaces, use virtual inheritance for every use of the interface class. – Pete Becker Jul 26 '21 at 21:52

2 Answers2

3

The problem in the code in the question is that C2 has two bases of type I1. One comes from the C1 base class, and the other comes from the I2 base class. C1 overrides f1() from its I1 base class, but there is no overrider for f1() in the I2 base.

The analog of a Java interface in C++ is a virtual base class. Change your hierarchy to

class I1 {
public:
  virtual void f1() = 0;
};
class I2 : public virtual I1 {
public:
  virtual void f2() = 0;
};
class C1 : public virtual I1 {
public:
  void f1() override {}
};
class C2 : public virtual I2, public C1 {
public:
  void f2() override {}
};
int main()
{
  C2* c2 = new C2;
}

That is, every inheritance from either of your interface classes should be virtual.

Once you've done that, there is only one I1 base class. The override of f() that comes from the C1 base overrides the f1() that comes from I1.

Pete Becker
  • 74,985
  • 8
  • 76
  • 165
  • Right. Read the last part of my question. – Wheezil Jul 26 '21 at 20:57
  • Happy to accept this as answer if you can provide a reference to why this use of the "dread diamond" is acceptable. There are a LOT of articles just saying "never do that". – Wheezil Jul 26 '21 at 21:32
  • 1
    “Dread diamond” is a scare-term that comes from people who tried to write code using multiple inheritance by applying bromides rather than understanding how multiple inheritance works in C++. Java programmers, in particular, get multiple inheritance in C++ wrong. – Pete Becker Jul 26 '21 at 21:50
1

Yes, it is possible. In order to share a base across the hierarchy, you have to use virtual inheritance:

struct I1 {
  virtual void f1() = 0;
};
struct I2 : virtual I1 {
  virtual void f2() = 0;
};
struct C1 : virtual I1 {
  void f1() override {}
};
struct C2 : I2, C1 {
  void f2() override {}
};
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Right. Read the last part of my question. – Wheezil Jul 26 '21 at 20:57
  • Happy to accept this as answer if you can provide a reference to why this use of the "dread diamond" is acceptable. There are a LOT of articles just saying "never do that". – Wheezil Jul 26 '21 at 21:32