4

This code doesn't compile because of §10.3/2, i.e., virtual function A::f has more than one final overrider in D.

#include <iostream>

class A { public: virtual void f(){ std::cout << "A::f" << '\n'; } };
class B : public virtual A { public: void f(){ std::cout << "B::f" << '\n'; } };
class C : public virtual A { public: void f(){ std::cout << "C::f" << '\n'; } };
class D : public B, public C { };

int main()
{
    D d;
    d.f();
}

But contrary to my expectations this code compiles. Given that §10.3/2 contains this sentence, For convenience we say that any virtual function overrides itself, it seems to me we have here the same problem mentioned above, i.e., the virtual function A::f has more than one final overrider in D, that is, A::f and C::f. As a matter of fact the call d.f() invokes C::f. Why is that?

#include <iostream>

class A { public: virtual void f(){ std::cout << "A::f" << '\n'; } };
class B : public virtual A {};
class C : public virtual A { public: void f(){ std::cout << "C::f" << '\n'; } };
class D : public B, public C { };

int main()
{
    D d;
    d.f();
}
Wake up Brazil
  • 3,421
  • 12
  • 19
  • g++ 4.8.1 : "error: request for member ‘f’ is ambiguous" – Kiroxas May 12 '14 at 14:11
  • @Kiroxas For me it compiles both in clang and g++. (http://coliru.stacked-crooked.com/a/f07e74ea8a5a752f) – Wake up Brazil May 12 '14 at 14:14
  • I assume you understand why it should work, you are just curious about the language in the standard and how it could be interpreted to mean what it should mean? – Yakk - Adam Nevraumont May 12 '14 at 14:23
  • @Yakk Not exactly. From what I'm reading in §10.3/2 this code shouldn't compile, as explained above. – Wake up Brazil May 12 '14 at 14:27
  • what about the comment in the standard about your example : // OK: A::f and C::f are the final overriders for the B and C subobjects, respectively – Kiroxas May 12 '14 at 14:28
  • @Kiroxas It was the comment that made me investigate this in more detail. Also, anything said in an example is not considered normative. – Wake up Brazil May 12 '14 at 14:33
  • @Cubbi To be honest I didn't understand the answer in the alluded question and it also doesn't say anything about the phrase `For convenience we say that any virtual function overrides itself` in §10.3/2, which an important point of my argument. – Wake up Brazil May 12 '14 at 17:50
  • @WakeupBrazil I believe the answer there explains the intent in sufficient detail. In the standard, the intent is demonstrated through the examples: an interpretation of the wording that renders the examples ill-formed is obviously not the expected interpretation. – Cubbi May 12 '14 at 18:05

1 Answers1

0

Note that the notion of final overrider applies to a particular object, not to a class:

A virtual member function C::vf of a class object S is a final overrider unless the most derived class (1.8) of which S is a base class subobject (if any) declares or inherits another member function that overrides vf. In a derived class, if a virtual member function of a base class subobject has more than one final overrider the program is ill-formed.

Bold text is mine.

The example in the standard doesn't use virtual inheritance. There are two A subobjects within D - let's call them A1 and A2. A::f is a final overrider for A::f in A1 and B. C::f is a final overrider in C (A2 doesn't have a final overrider). The definition of D is well-formed because there is no single base class subobject for which A::f would have more than one final overrider in D.

In your example, with virtual inheritance, there is only one A object, which is a base class subobject of both B and C. A::f is not a final overrider in B, because the most derived class of which B is a base class subobject (namely, D) inherits another member function that overrides A::f (namely, C::f).

So, once again, there is no single base class subobject for which A::f would have more than one final overrider in D. C::f is a final overrider for A::f in C and D objects; A and B objects don't have a final overrider.

And indeed, this code calls C::f:

// Setup the same as in the OP's example:
D d;
B* pB = &d;
pB->f();  // calls C::f, not A::f
Igor Tandetnik
  • 50,461
  • 4
  • 56
  • 85