Search code examples
c++diamond-problem

Why can't I use `using` in a multiple inheritance c++?


I tried to implement some interfaces and their children. This is my idea:

         Interface
         /       \
  Interface2    InterfaceDefination
        |        /
  Interface2Defination  

And this is my code:

#include <iostream>

class Interface {
public:
  virtual void method1() = 0;
  virtual void print() = 0;
};

class Interface2 : public Interface {
public:
  virtual void method2() = 0;
};

class InterfaceDefination : public Interface {
public:
  virtual void method1() override {
    std::cout << "method1 from InterfaceDefination\n";
  }
};

class Interface2Defination : public Interface2, public InterfaceDefination {
public:
  using InterfaceDefination::method1;
  virtual void print() override {
    std::cout << "print from Interface2Defination\n";
  }
  virtual void method2() override {
    std::cout << "method2 from Interface2Defination\n";
  }
};

int main() {
  Interface2Defination c;
  c.method1();
  c.method2();
  c.print();
}

My expected output is:

method1 from InterfaceDefination
method2 from Interface2Defination
print from Interface2Defination

But accidentally i received these errors:

main.cpp:33:24: error: variable type 'Interface2Defination' is an abstract class
  Interface2Defination c;
                       ^
main.cpp:5:16: note: unimplemented pure virtual method 'method1' in 'Interface2Defination'
  virtual void method1() = 0;
               ^
1 error generated.
make: *** [<builtin>: main.o] Error 1
exit status 2

https://godbolt.org/z/9ncoGfn4P

In this case, the error means using keyword is not making method method1 usable in class Interface2Defination. What should i do with it?


Solution

  • A using declaration is a declaration. It is not a definition. It does not define anything "new". What it does is declare: ok, I have a symbol X, but it really refers to X in my parent class (for this version of a using declaration).

    You might ask what's the point, aren't you inheriting X from your parent class in the normal fashion. Yes, that's true, but it's not the same thing, but the technical differences are mostly immaterial here.

    What's also important here is that the shown diagram is misleading. It is a popular way to portray (non-virtual) diamond inheritance, but every time it's shown, it is 100% wrong. This is because Interface2Defination does not inherit one instance of Interface, but two. This is a more accurate inheritance diagram:

      Interface          Interface
           |                |
      Interface2    InterfaceDefination
            |        /
      Interface2Defination
    

    For each one of the two instances of Interface parent classes, only one of its abstract methods gets defined and overridden, hence the shown code is ill-formed.

    The using declaration does not define the imported method in the child class, and formally override it. A using declaration does not "count" for the purpose of overriding an abstract method. Only formally defining an inherited abstract method does that, not declaring one.