8

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?

Marek R
  • 32,568
  • 6
  • 55
  • 140
VN VNA
  • 117
  • 1
  • 7
  • 3
    Your code form an *Y* (i.e your class have 2 distinct `Interface` ) not a *diamond* (which requires virtual inheritance). – Jarod42 Feb 08 '22 at 12:07
  • 3
    You want a diamond-pattern inheritance architecture. Both Interface2 and InterfaceDefinition should inherit from Interface virtually. E.g. `class Interface2 : virtual public Interface ` and `class InterfaceDefination : virtual public Interface` . That ties both bases to the same root `Interface`, and thus the `using` will stick to the one (and only) `method1` root pure decl. – WhozCraig Feb 08 '22 at 12:08
  • You are inheriting twice from Interface. Are you sure this is what you intend to do? – Eric Feb 08 '22 at 12:09
  • You can get detailed information about "using" keyword at https://stackoverflow.com/questions/54038898/applying-using-keyword-on-c-pure-virtual-function – Amol Saindane Feb 08 '22 at 12:14

2 Answers2

10

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.

Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • The "100% wrong" portrayal is because it shows the OOP inheritance structure, but not the C++ layout which clearly shows that the CLASS inheritance is resulting in two separate class INSTANTIATIONS. I concur with it being "100% wrong" because it causes confusion. Curse you UML! – Eljay Feb 08 '22 at 15:28
3

As it stands, there are two Interface::method1s in Interface2Defination, and only one of them has an implementation. If you virtually inherit Interface, you have one, with a definition.

#include <iostream>

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

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

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

class Interface2Defination : public Interface2, public InterfaceDefination {
public:
  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();
}
Caleth
  • 52,200
  • 2
  • 44
  • 75