2016-07-08 4 views
4

Учитывая следующее:Выбор, какой базовый класс, чтобы переопределить метод

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 переопределяет как TaggedObserver<0>::Observe и TaggedObserver<1>::Observe.
Есть ли способ предоставить другое переопределение для каждого базового класса?
Обоснование: я хочу, чтобы класс мог наблюдать два источника уведомления одного и того же типа с разными действиями для каждого источника, не прибегая к использованию источника в параметре, а затем проверяя его на if/switch.

+0

'TaggedObserver <0> и' 'TaggedObserver <1> такие же класса. Почему бы не сделать два дочерних класса? –

+2

@ JimV: Это не так. Точка их шаблонов - это дифференциация базовых классов. – Dani

+1

@JimV: 'TaggedObserver <0>' и 'TaggedObserver <1>' может быть основан на том же классе * template *, но они являются разными * экземплярами * классов. Точно так же, как 'std :: vector ' и 'std :: vector ' - это разные классы. Значения параметров шаблона являются частью типа класса и производят разные RTTI-результаты. –

ответ

3

Для того, чтобы обеспечить различное переопределение, необходимо определить различные производные классы, например:

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 ... 
    } 
}; 

Однако, если вы хотите Thing::Observe() получить Parameter первым и направить его на соответствующий базовый класс, вы можете «т избежать использования if оператора (или эквивалента) в Thing, так как он наследует несколько копий TaggedObserver::Observe() и должен решить, какой из них назвать:

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); 
    } 
}; 

Или вы звоните только они оба, безусловно, и пусть они понять, что делать:

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

Реализовать их в TaggedObserver (указать явную специализацию в случае необходимости), в качестве примера:

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); 
    } 
}; 

Тогда, вы можете специализировать Thing как Thing<0, 1> и вызвать правильную функцию, используя thing.Observe<1>(p).

EDIT

Цель этого редактирования, чтобы показать новый пример кода, который более или менее один выше, даже если слегка изменен.
Надеюсь, это поможет OP. Основная идея - объединить идиому CRTP, виртуальные методы и наследование.

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); 
    } 
}; 

Обратите внимание, что friend декларация позволяет TaggedObserver ей получить доступ к закрытым членам Thing.

Таким образом, реализации Observe в TaggedObserver s могут получить доступ к общедоступным, защищенным и закрытым членам от Thing, как указано в комментариях.

Наконец, вы можете специализироваться на TaggedObserver, если необходимо, чтобы обеспечить различные реализации для Observe.
В качестве примера:

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 
    } 
}; 
+0

@kfsone из-за пропущенного 'public', добавил его ... – skypjack

+1

Я устал - я мог бы поклясться, что он уже был там :) – kfsone

+0

Ницца. Есть ли способ переместить 'template void Observe (Параметр p)' где-то выше, чтобы не дублировать его в каждом классе Thing? Это то, что я пытался сделать в [случае специализации функции члена шаблона, которая работает на msvc, а не на других) (http://stackoverflow.com/questions/38276005/case-of-template-member-function-specialization-that- works-on-msvc-not-others), но не нашел правильного заклинания. – dxiv