Факторизация реализаций чистых виртуальных функций для подмножества классов - PullRequest
3 голосов
/ 12 августа 2011

Предположим, у меня есть эта иерархия классов:

class A {
public:
    virtual void foo(Base *b) = 0;
};

class B {
public:
    virtual void bar() = 0;
};


class C : public A, public B {
public:
    virtual void viz() = 0;
};

class E : public C {
public:
    /** Some non-pure virtual functions, does not implement foo */
};

class F : public E {
    /** does not implement foo */
}

class G : public E {
    /** does not implement foo */
}

Теперь я хотел бы определить производные "шаблонизированные" версии F и G, которые объявляют метод foo(T *s) и где реализация foo(Base *s) просто вызывает foo (T * s), применяя static_cast.

Более того, я не хочу раскрывать аргументы шаблона для A, поскольку пользователи этих интерфейсов (A, B, C, E) не должны подвергаться воздействию параметров шаблона (я пишу плагин)архитектура и пользовательские типы, которые порождают данный интерфейс, передаются взад и вперед от плагинов к базовой системе.)

То, что я думал, было определить этот класс:

template <typename T>
class GA : public A{
...
}

и определить«шаблонизированные» производные F и G выглядят так:

template <typename T>
class GF : public F, public GA {
...
}

проблема в том, что GF видит два разных объявления foo () (одно дано A, другое GA), поэтому компилятор выдает меняошибка, когда я пытаюсь создать экземпляр GF, потому что foo (Base) не определен.

Как я могу решить эту проблему?

Ответы [ 5 ]

4 голосов
/ 12 августа 2011

Зачем вам нужно множественное наследование?Почему не просто class C : public Impl { ... }?

2 голосов
/ 12 августа 2011

Я бы предложил что-то другое. Объявите функцию как чисто виртуальную функцию, а затем просто тривиально реализуйте ее в производных классах, перенаправив вызов:

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

class B : public A {
   void foo() { A::foo(); }
};

Но это не значит, что вы не можете достичь того, что пытаетесь сделать. Если вы хотите реализовать эту операцию в другом классе и использовать наследование, вы можете сделать это двумя различными способами:

Промежуточный тип:

Реализуйте промежуточный тип, который обеспечивает функциональность, тогда каждый производный тип может расширять либо исходный A, либо промежуточный I тип:

class I : public A {
public:
   void foo() {}
};
class B : public I {};      // inherits the implementation
class C : public A {        // needs to provide it's own implementation
   void foo() {}
};

Виртуальное наследование:

В качестве альтернативы вы можете использовать виртуальное наследование (из трех вариантов я бы избежал этого). Таким образом, вы можете создать иерархию, в которой V фактически наследует от A и обеспечивает реализацию. Остальные классы могут либо наследоваться от A напрямую, либо виртуально наследоваться от A, а также наследоваться от V. Используя виртуальное наследование, вы гарантируете, что в вашей иерархии есть единственный подобъект A:

class V : public virtual A {
public:
   void foo() {}
};
class B : public virtual A, V { // Inheritance from V is a detail, can be private
                                // no need to provide foo
};

Обратите внимание, что этот подход является избыточным для этой конкретной ограниченной проблемы, и что у него есть побочные эффекты (расположение объектов будет другим, и они будут немного больше по размеру). Опять же, если вы не хотите смешивать и сопоставлять функции из разных классов sibling , я бы избегал этого.

Что не так в вашем коде

Проблема в вашем коде заключается в том, что вы наследуете от A несколько раз, от одного до D и от другого напрямую. В то время как в пути из D виртуальная функция более не является чистой, в прямом пути из A виртуальная функция все еще не определена.

Это также вызывает вторую проблему в сообщении об ошибке: неоднозначность. Поскольку существует два подобъекта A, при попытке вызвать foo для самого производного типа компилятор не может определить, для какого из двух подобъектов A вы хотите выполнить запрос. Подобные проблемы неоднозначности возникнут, если вы попытаетесь преобразовать самый производный тип в A напрямую, так как компилятор не будет знать, к какому A вы хотите обратиться.

2 голосов
/ 12 августа 2011

Думаю, проблема в том, что Impl наследует класс A, а класс C наследует Impl и A. Это означает, что будет два экземпляра класса A, где первый A foo реализован с помощью Impl, а второй - все еще чисто виртуальный. Виртуальное наследование может решить эту проблему, например:

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

class Impl : virtual public A {
public:
    virtual void foo () {}
};

class C
    : virtual public A
    , public Impl
{
};
1 голос
/ 12 августа 2011

Impl не реализует метод A в D и C, это совершенно другая база.

Вы должны получить Impl из A, а затем вывести каждый класс этого подмножества из Impl вместо A.

0 голосов
/ 12 августа 2011

Если вы хотите наследовать от A и Impl, вам нужно использовать виртуальное наследование.

Редактировать:

Подробнее см. В ответе Влада.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...