#include <iostream> #include <string> class A { public: virtual void print()const { std::cout << "from A" << std::endl; } }; class B : public A { public: void print()const override{ std::cout << "from B" << std::endl; } }; class C : public A { public: void print()const override{ std::cout << "from C" << std::endl; } }; class D :public B, public C { public: using C::print; }; int main() { D d{}; d.print(); //from B return 0; } 

And why it is impossible to use the using declaration with virtual inheritance?

  • In your example, there is no virtual inheritance. And displays "from C" . And nothing is impossible. - VTT
  • strange in my vs2017 displays from B, and about virtual inheritance, it is not here, but if it were, using would not work - Ruslan
  • Hmm, really. It looks like a bug in the compiler. As for virtual inheritance, in this situation class D will have to implement the print function itself, since it will directly inherit and initialize an instance of class A The using directive only affects the detection of names; it does not allow the implementation of methods to be added to a class. - VTT
  • First, the problem does not reproduce. Secondly, what do the strange phrases mean "you cannot use a using declaration with virtual inheritance", "if it were [virtual inheritance], using would not work"? Where did you get this from? If class A inherited virtually, then there would be an error in the code about the absence of a final overrider in D , which is not related to using at all. - AnT

1 answer 1

If we are talking about Visual Studio, then this is apparently a compiler bug.

For some reason, the Visual Studio d.print() decides in such a situation to implement the d.print() call as a full-fledged virtual call through the class D virtual functions table. (In this, incidentally, there is still no error.) But to resolve this virtual call, he certainly takes the first table in an object of type D (from the first base in the list of databases), for which reason such virtual calls always fall into B::print . If you swap the bases in the class D declaration, then C::print will be called all the time.

If you make the print function non-virtual, then the problem disappears. If you explicitly make the call non-virtual, i.e. instead of d.print() calling dD::print() , then the problem disappears.


This is actually an interesting topic. The d.print() call is a virtual call from the point of view of the language and should be resolved based on the dynamic type of the object d . Those. The final overrider must be taken from class D And who is the final overrider in D ? (More precisely, from which chain of bases should it be selected: A->B->D or A->С->D ?). Using-declaration cannot be used to resolve this issue. The standard has a good example on this topic.

 struct A { virtual void f(); }; struct B : virtual A { virtual void f(); }; struct C : B , virtual A { using A::f; }; void foo() { C c; cf(); // calls B​::​f, the final overrider cC::f(); // calls A​::​f because of the using-declaration } 

Note that cf() should call B::f() despite the presence of using A::f in C

Apparently, in our example, everything starts with the name lookup, which should use the using-declaration using C::print and localize the review in the A->C inheritance branch, where the final overrider is unambiguous.

  • Judging by the code from the standard, then in the original UB code. If using A::f not final overrider, then using C::print; it is not. But in this case, the class has C 2 final overriders, which is UB. But this is of course confused by the standard, because in order to be final overrider it is enough to declare an entity, which, in fact, using and does. - ixSci
  • Interestingly, in earlier standards there seemed to be a line like this: “It’s not a problem.” ” That explained a lot. But this line is no more. - ixSci