Choosing which base class to override method of

[亡魂溺海] 提交于 2019-12-07 08:41:02

问题


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 TaggedObservers to access private members of Thing.

This way, implementations of Observe in TaggedObservers 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!