@πάντα ῥεῖ's answer describe one workaround, but possible this is not what OP is after here. Also, as my comment describe under the answer, the approach in the answer might give unexpected results e.g. when invoking node->notifyObservers(obj)
:
Note that in this particular example, Node* node = new
ObservableNode();
will mean node->notifyObservers(obj)
will invoke
Node::notifyObservers(IObject*)
and not
SingleObservable::notifyObservers(IObject*)
, which might be
unexpected, considering we instantiate an ObservableNode
object
which specifies using SingleObservable::notifyObservers;
.
In OP's original code, we are suffering from multiple inheritance ambiguity, as we are not using virtual
inheritance when Node
and SingleObservable
(and MultiObservable
) derives from IObservable
:
class SingleObservable: public IObservable {
public:
virtual ~SingleObservable() {};
void notifyObservers(IObject*) override {
//some implementaiton
};
};
class Node: public IObservable {
public:
virtual ~Node() {};
};
Meaning our the object's memory layout, w.r.t. inheritance, of ObservableNode
to looks like the following
IObservable IObservable
| |
Node SingleObservable
\ /
ObservableNode
whereas, in this context, we are likely to want an object's memory layout looking as follows
IObservable
/ \
Node SingleObservable
\ /
ObservableNode
If we were to correct this, Node
can stay abstract, and a call to node->notifyObservers(obj)
with node
as OP's example will result in invocation of SingleObservable::notifyObservers
, as might have been expected.
class Node: public virtual IObservable {
// ↑↑↑↑↑↑↑
public:
virtual ~Node() {};
};
class SingleObservable: public virtual IObservable {
// ↑↑↑↑↑↑↑
public:
virtual ~SingleObservable() {};
void notifyObservers(IObject*) override {
std::cout << "SingleObservable::notifyObservers";
};
};
struct DummyObj : public IObject {};
int main() {
Node* node = new ObservableNode();
DummyObj obj;
node->notifyObservers(obj); // SingleObservable::notifyObservers
}
Note that we not need virtual
inheritance for when ObservableNode
derives from Node
and SingleObservable
.
Finally, if we'd want Node
be non-abstract (specifically, to provide an override of void notifyObservers(IObject*)
), then ObservableNode
must provide it's own (final
) override of it, as we will otherwise inherit two final overrides of it in ObservableNode
(one from Node
and one from SingleObservable
). In this case, ObservableNode
could simply define its own override which explicitly calls the base class of choice, e.g.
class Node: public virtual IObservable {
public:
virtual ~Node() {};
void notifyObservers(IObject*) override {
std::cout << "Node::notifyObservers";
};
};
class SingleObservable: public virtual IObservable {
public:
virtual ~SingleObservable() {};
void notifyObservers(IObject*) override {
std::cout << "SingleObservable::notifyObservers";
};
};
class ObservableNode: public Node, public SingleObservable {
public:
virtual ~ObservableNode() {};
// Non-ambiguous final override in ObservableNode.
// We could use `override` specifier here, but we might as well
// use `final`, if we are not expecting something to derive from ObservableNode.
void notifyObservers(IObject* obj) final {
SingleObservable::notifyObservers(obj);
};
};
struct DummyObj : public IObject {};
int main() {
Node* node = new ObservableNode();
DummyObj obj;
node->notifyObservers(obj); // SingleObservable::notifyObservers
}
See ISO C++ FAQ - Inheritance — Multiple and Virtual Inheritance for details on the diamond inheritance structure and virtual inheritance.