Как переопределить функцию в другом базовом классе?

Я не совсем уверен, какую терминологию использовать, но вот мой пример:

class Base {
public:
    virtual void test() = 0;
};

class Mixin {
public:
    virtual void test() { }
};

class Example : public Base, public Mixin {
};

int main(int argc, char** argv) {
    Example example;
    example.test();
    return 0;
}

Я хочу, чтобы мой Mixin класс реализовал чистую виртуальную функцию Base::test, но когда я компилирую это, он говорит:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:15:13: error: cannot declare variable ‘example’ to be of abstract type ‘Example’
     Example example;
             ^
test.cpp:11:7: note:   because the following virtual functions are pure within ‘Example’:
 class Example : public Base, public Mixin {
       ^
test.cpp:3:18: note:    virtual void Base::test()
     virtual void test() = 0;
                  ^
test.cpp:16:13: error: request for member ‘test’ is ambiguous
     example.test();
             ^
test.cpp:8:18: note: candidates are: virtual void Mixin::test()
     virtual void test() { }
                  ^
test.cpp:3:18: note:                 virtual void Base::test()
     virtual void test() = 0;
                  ^

Я могу добавить выражение using, чтобы оно не было двусмысленным:

class Example : public Base, public Mixin {
public:
    using Mixin::test;
};

Но там сказано, что я до сих пор не реализовал это:

test.cpp: In function ‘int main(int, char**)’:
test.cpp:17:13: error: cannot declare variable ‘example’ to be of abstract type ‘Example’
     Example example;
             ^
test.cpp:11:7: note:   because the following virtual functions are pure within ‘Example’:
 class Example : public Base, public Mixin {
       ^
test.cpp:3:18: note:    virtual void Base::test()
     virtual void test() = 0;
                  ^

Можно ли сделать это?

Я знаю, что один из вариантов - сделать Mixin наследовать от Base, но в моем случае есть несколько производных классов, и у них нет общего предка.


person Brendan Long    schedule 22.10.2013    source источник
comment
«Я знаю, что можно сделать так, чтобы Mixin унаследовал от Base». ИМХО, это противоречит моему пониманию «Mixins» ...   -  person πάντα ῥεῖ    schedule 23.10.2013
comment
@ g-makulik Я хотел сказать, что в подобных ситуациях техническое решение проблемы обычно состоит в том, чтобы сделать класс не миксином, но в этом случае это не работает. Настоящий класс не называется Mixin.   -  person Brendan Long    schedule 23.10.2013
comment
Просто выстрел в темноте: может помочь CRTP?   -  person πάντα ῥεῖ    schedule 23.10.2013
comment
На ум приходят другие вещи: Дизайн на основе политик , двойная отправка ...   -  person πάντα ῥεῖ    schedule 23.10.2013


Ответы (2)


Вы не можете напрямую иметь класс, переопределяющий метод, не относящийся к его базовому классу. Но вы можете сделать это как бы окольными путями. Я представлю два таких подхода - я предпочитаю второй.

Подход 1

Это описано Дэниэлом Полом в сообщении на thinkbottomup.com.au под названием C ++ Mixins - Повторное использование через наследование - это хорошо ... если все сделано правильно.

В вашем случае это будет выглядеть так:

class Base {
public:
    virtual void test() = 0;
};

template <typename T>
class Mixin : public T {
public:
    virtual void test() override { /*... do stuff ... */ }
};

class UnmixedExample : public Base {
    /* definitions specific to the Example class _not_including_
       a definition of the test() method */
};

using Example = class Mixin<UnmixedExample>;

int main(int argc, char** argv) {
    Example{}.test();
    return 0;
}

Подход 2: CRTP!

CRTP - это «Любопытно повторяющийся шаблон шаблона» - обязательно перейдите по этой ссылке, если вы не видели ее раньше. При таком подходе мы будем использовать спецификатор наследования virtual, чтобы избежать двусмысленности, и, в отличие от предыдущего подхода, мы не будем менять порядок наследования классов Mixin и Example.

class Base {
public:
    virtual void test() = 0;
};

template <typename T>
class Mixin : virtual T {
public:
    virtual void test() override { /*... do stuff ... */ }
};

class Example : public virtual Base, public virtual Mixin<Base> {
    /* definitions specific to the Example class _not_including_
       a definition of the test() method */
};

int main(int argc, char** argv) {
    Example{}.test();
    return 0;
}

Обратите внимание на оба решения:

  • Разве не любопытно, как CRTP постоянно повторяется? :-)
  • Я использовал код C ++ 11 в педагогических целях, но то же самое будет работать и на C ++ 98.
person einpoklum    schedule 27.04.2017

Вы не можете иметь класс, отменяющий виртуальную функцию несвязанного класса. Есть разные способы обойти эту проблему. Вы можете сделать миксин шаблоном, который является производным (виртуально) от аргумента типа и использовать его как class Example : public virtual Base, Mixin, или вы можете добавить код в последний класс для отправки на микширование:

void Derived::test() { Mixin::test(); }
person David Rodríguez - dribeas    schedule 22.10.2013
comment
Дэвид, как ученый мудрец с золотым значком, не могли бы вы взглянуть на мой альтернативный ответ? - person einpoklum; 28.04.2017
comment
@einpoklum: Второй подход тот же, что я упоминал в текстовом абзаце. Вам не нужно виртуальное наследование от подмешивания, и вам не нужен CRTP. Первый подход, который у вас есть, - это расширение иерархии, а не использование добавлений, он также будет работать, предлагая другие преимущества / недостатки по сравнению со вторым подходом. Я все еще чувствую, что приближение к идеалу микширования - это оплата дополнительной строки, необходимой для пересылки в миксин. - person David Rodríguez - dribeas; 02.05.2017