Необходимо преодолеть обходной путь - PullRequest
4 голосов
/ 22 августа 2011

Мне трудно найти (что я уверен, что это очень распространенный) шаблон проектирования, чтобы обойти следующую проблему.Рассмотрим этот фрагмент кода:

class AA {};
class BB : public AA {};

class A
{
public:
    virtual void foo(AA& aa) = 0;
};

class B : A
{
public:
    void foo(BB& bb){cout<<"B::foo"<<endl;}
};

int main()
{
    B b;
    BB bb;
    b.foo(bb);
}

Этот код не будет компилироваться, поскольку класс B не переопределяет чисто виртуальную функцию 'foo'.Компилятор рассматривает foo, который B объявляет только как перегрузку для foo, потому что ковариация не допускается во входных параметрах в переопределенных функциях.

Теперь я понимаю причину этого.Тот факт, что B наследуется от A, означает, что он должен иметь возможность обрабатывать любые вызовы foo с параметрами типа AA, а предыдущий код не дал никакой реализации для обработки любого типа параметра, кроме BB.

Конечно, я могпросто приведите aa к BB в реализации boo foo, но я ищу решение, которое сохраняет безопасность типов и фактически заставляет разработчика класса B также реализовать класс, который наследуется от AA, чтобы код компилировался.В идеальном мире я мог бы написать что-то похожее на этот псевдокод:

class A
{
public:
    abstract class AA{}; //indicates that any child of A must implement also child of AA
    abstract void foo(AA& aa);
};

class B : public A
{
public:
    class BB : AA{}; //will not compile without this
    void foo(BB& bb){cout<<"B::foo"<<endl;}
};

Есть ли способ достичь чего-то похожего в C ++?(возможно, это может быть сделано с помощью какого-либо объекта сопоставления без необходимости наследования)

Обратите внимание, что в действительности (в отличие от примера) наследование между BB и AA имеет решающее значение, так как у AA много детей, которые обладают многими качествамии, в конце концов, я хочу выполнить итерацию по вектору классов A и запустить 'foo' только с соответствующими параметрами (вектором AA)

Ответы [ 2 ]

8 голосов
/ 22 августа 2011

Чтобы обеспечить безопасность типов, вы должны использовать шаблоны вместо наследования.

class AA {};
class BB : AA {};

template <typename Managed> class FooManager {
    virtual void foo(Managed& m) { /* default implementation */ }
};

class B : public FooManager<BB> {
    void foo(BB bb) { cout << "B:foo()" << endl; }
};

Позже в коде, например, если вы хотите пройти массив,

template<typename Managed> void processAll(vector<Managed> v, FooManager<Managed> mgr) {
    for(Managed& m : v) mgr.foo(m);
}

B b;
vector<BB> bbs;
processAll(bbs, b);

Изменить: опечатка исправить

3 голосов
/ 22 августа 2011

вы можете посмотреть на шаблон посетителя.это общее решение проблем типа «двойная отправка» (отправка виртуальной функции на основе объекта и сообщения).

То есть, поместите foo() в посетителя и переименуйте A::foo() в A::Visit(FooVisitor& ):

edit: чтобы уточнить, это может помочь распутать цели ваших иерархий.Если вы думаете об этом, вы пытаетесь смоделировать отношения одной иерархии (AA и BB) в терминах другой (A и B).Это довольно неловко для модели, или даже думать о концептуально.

Чтобы перемоделировать это как посетителя, вы обычно превращаете одну из иерархий в один класс и вместо этого моделируете операций , которые вы можете выполнять над этим классом в иерархии алгоритмов с помощьюПосетитель.Это более надежно, потому что вынуждает вас явно реализовывать каждую комбинацию иерархических отношений, и прервется во время компиляции (хорошо), если вы позже измените иерархию.

class A; class B;
struct AVisitor 
{ 
    virtual ~AVisitor() { } 

    virtual void Visit(A& ) = 0;
    virtual void Visit(B& ) = 0;
};

class A
{
public:
    virtual ~A() { }

    virtual void Visit(AVisitor & visitor) { visitor.Visit(*this); }
};

class B : public A
{
public:
    virtual void Visit(AVisitor & visitor) { visitor.Visit(*this); }
};

struct PrintingVisitor : public AVisitor
{
    void Visit(A& a){cout<<"A::foo"<<endl;}
    void Visit(B& b){cout<<"B::foo"<<endl;}
};

int main()
{
    B b;
    PrintingVisitor printer;
    b.Visit(printer);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...