问题
Given the following:
class Observer
{
public:
virtual void Observe(Parameter p) = 0;
};
template<size_t Tag>
class TaggedObserver : public Observer { };
class Thing : public TaggedObserver<0>, TaggedObserver<1>
{
public:
virtual void Observe(Parameter p) override;
};
Thing::Observe
overrides both TaggedObserver<0>::Observe
and TaggedObserver<1>::Observe
.
Is there a way to provide a different override for each base class?
Rationale: I want the class to be able to observe two notification sources of the same type with different actions for each source without having to resort to pass the source in the parameter and then checking it in an if
/switch
.
回答1:
In order to provide different overrides, you need to define different derived classes, eg:
class Observer
{
public:
virtual void Observe(Parameter p) = 0;
};
template<size_t Tag>
class TaggedObserver : public Observer
{
};
class TaggedObserverZero : public TaggedObserver<0>
{
public:
virtual void Observe(Parameter p)
{
// do something ...
}
};
class TaggedObserverOne : public TaggedObserver<1>
{
public:
virtual void Observe(Parameter p)
{
// do something else ...
}
};
However, if you want Thing::Observe()
to receive the Parameter
first and dispatch it to the appropriate base class, you can't avoid using an if
statement (or equivalent) in Thing
, since it inherits multiple copies of TaggedObserver::Observe()
and needs to decide which one to call:
class Thing : public Observer, TaggedObserverZero, TaggedObserverOne
{
public:
virtual void Observe(Parameter p)
{
if (some condition)
TaggedObserverZero::Observe(p);
else if (some other condition)
TaggedObserverOne::Observe(p);
}
};
Or, you call just them both unconditionally and let them figure out what to do:
class TaggedObserverZero : public TaggedObserver<0>
{
public:
virtual void Observe(Parameter p)
{
if (some condition)
// do something ...
}
};
class TaggedObserverOne : public TaggedObserver<1>
{
public:
virtual void Observe(Parameter p)
{
if (some other condition)
// do something else ...
}
};
class Thing : public Observer, TaggedObserverZero, TaggedObserverOne
{
public:
virtual void Observe(Parameter p)
{
TaggedObserverZero::Observe(p);
TaggedObserverOne::Observe(p);
}
};
回答2:
Implement them in TaggedObserver
(provide explicit specialization if needed), as an example:
class Observer {
public:
virtual void Observe(Parameter p) = 0;
};
template<size_t Tag>
class TaggedObserver : public Observer {
public:
void Observe(Parameter p) override { }
};
template<std::size_t... I>
class Thing : public TaggedObserver<I>... {
public:
Thing(): TaggedObserver<I>{}... {}
template<std::size_t N>
void Observe(Parameter p) {
TaggedObserver<N>::Observe(p);
}
};
Then, you can specialize Thing
as Thing<0, 1>
and invoke the right function using thing.Observe<1>(p)
.
EDIT
The purpose of this edit is to show a new example code, that is more or less the one above even if slightly modified.
I hope this can help the OP. The basic idea is to combine CRTP idiom, virtual methods and inheritance.
class Observer {
public:
virtual void Observe(Parameter p) = 0;
};
template<template T, size_t Tag>
class TaggedObserver : public Observer {
public:
void Observe(Parameter p) override {
T *t = static_cast<T*>(this);
// Now use whatever you want from T, that is Thing in this example
}
};
template<std::size_t... I>
class Thing : public TaggedObserver<Thing<I...>, I>... {
template<std::size_t J>
friend class TaggedObserver<Thing<I...>, J>;
public:
Thing(): TaggedObserver<Thing<I...>, I>{}... {}
template<std::size_t N>
void Observe(Parameter p) {
TaggedObserver<Thing<I...>, N>::Observe(p);
}
};
Note that the friend
declaration allows TaggedObserver
s to access private members of Thing
.
This way, implementations of Observe
in TaggedObserver
s can access public, protected and private members from Thing
, as requested in the comments.
Finally you can specialize TaggedObserver
if needed, so as to provide different implementations for Observe
.
As an example:
template<template T, size_t Tag>
class TaggedObserver;
template<template T>
class TaggedObserver<T, 0>: public Observer {
public:
void Observe(Parameter p) override {
T *t = static_cast<T*>(this);
// Now use whatever you want from T, that is Thing in this example
// Put here the code of the specialization for Tag 0
}
};
template<template T>
class TaggedObserver<T, 1>: public Observer {
public:
void Observe(Parameter p) override {
T *t = static_cast<T*>(this);
// Now use whatever you want from T, that is Thing in this example
// Put here the code of the specialization for Tag 1
}
};
来源:https://stackoverflow.com/questions/38271718/choosing-which-base-class-to-override-method-of