Diamond-inheritance scenario compiles fine in G++, but produces warnings/errors in VC++/Eclipse-Collection of common programming errors
I have a base class ‘Base’, which is a pure virtual class:
class Base {
public:
virtual void A() = 0;
virtual void B() = 0;
virtual ~Base() { } // Eclipse complains that a class with virtual members must have virtual destructor
};
I also have 2 other classes, one of them implements A() and the other one implements B():
class DerivedA : public virtual Base
{
public:
virtual void A() {
printf("Hello from A");
}
};
class DerivedB : public virtual Base
{
public:
virtual void B() {
printf("Hello from B");
}
};
The virtual keyword in the declaration should solve the diamond problem.
Now I would like to combine the two classes into another class, so that both A() and B() are implemented, like this:
class DerivedC: public DerivedA, public DerivedB {
// Now DerivedA and DerivedB are combined
};
// Somewhere else in the code
DerivedC c;
c.A();
c.B();
The problem: Even though G++ compiles the code just fine, Eclipse gives an error: The type 'DerivedC' must implement the inherited pure virtual method 'Base::B'
. When compiling with visual studio, I get 2 warnings:
warning C4250: 'DerivedC' : inherits 'DerivedB::DerivedB::B' via dominance
warning C4250: 'DerivedC' : inherits 'DerivedA::DerivedA::A' via dominance
So the question is: what is the correct way of doing this? Does the code above produce undefined behavior?
Note: The title may be a little misleading, I have no idea what a good title for this question would be.
-
What is the correct way of doing this? Does the code above produce undefined behavior?
The code is perfectly valid. There is no Undefined Behavior here.
An unqualified call ofA()
through aDerivedC
class object, will always callDerivedA::A()
, while an unqualified call ofB()
through aDerivedC
class object, will always call theDerivedB::B()
instance.Visual C++ gives you a warning because your code uses a less commonly known feature of virtual Inheritance which may not be obvious to most common users and might surprise them. In this case, the warning should be taken as an Informative Nitpick rather than a warning.
Note that the C++ Standard does not restrict compilers from emitting informative warnings for perfectly valid code. The documentation for warning C4250 gives an example which tells you why visual C++ chooses to give this warning.
-
You might want to try this :
class DerivedC: public DerivedA, public DerivedB { public: using DerivedA::A; using DerivedB::B; };
I can’t test with Eclipse or VC++ myself…
-
I don’t know why a compiler would complain about any of this; this is just the standard mixin technique. Classes
Base
,DerivedA
andDerivedB
are abstract, and can’t be instantiated, but that’s usually the case for mixins. The whole point of a mixin is that it doesn’t implement all of the interface. AndDerivedC
implements bothA()
andB()
through its inherited members.If a compiler refuses to compile this code, it is broken.
As for warnings… a compiler is free to warn about anything it pleases:
-
There is no requirement that a class with virtual members have a virtual destructor. In practice, it’s usually a good idea, however (unless the destructor is protected), and a compiler warning is appropriate.
-
The warnings from Visual Studio are “informative”, I guess, but this is the way the language is designed to work. And it’s certainly not something to avoid. For that matter, I don’t think that dominance actually plays a role here, since the functions in
Base
are pure virtual. What Visual Studios seems to be trying to say is that inDerivedC
, the actual overload ofA()
isDerivedA::A()
, and notBase::A()
. Which seems to be what one would intuitively expect to me; the rules concerning dominance are really just a formal statement of what one would intuitively expect.
Anyway, I’d definitely turn the warning about dominance off. There’s certainly nothing to worry about in that respect. And I’d complain loudly about a compiler which didn’t compile the code.
-
-
Your Base class is abstract : it can’t be instantiated. The B and A class are also abstract because they implement only one method.
The two solutions are, in file
DerivedC.cpp
void DerivedC::A(){ DerivedA::A(); } void DerivedC::B(){ Derived:B(); }
Or you can use
using
keyword in your header-file:class DerivedC: public DerivedA, public DerivedB { public: using DerivedA::A; using DerivedB::B; };
Originally posted 2013-11-09 22:48:27.