WedX - журнал о программировании и компьютерных науках

переопределить базовый метод шаблона в производном классе?

Предположим, у меня есть базовый класс, как показано ниже:

template <typename T>
class Base {
    // implementation
    void do_something() { /* ... */ } ;
};

затем я создаю класс Derived, как показано ниже, и переопределяю метод do_something():

template <typename T>
class Derived : public Base<T> {
    // implementation
    void do_something() { /* ... */ } ;
};

Я знаю, что виртуализация не работает в шаблонах классов, и я просто скрываю реализацию методов. но я хочу хранить кучу производных классов и базовых классов в векторе (я не хочу использовать стирание типов или полиморфизм),

мой вопрос: учитывая, что static_cast из класса Derived в базовый класс дает мне do_something базового класса, есть ли способ сохранить их как базовые классы, в то время как каждый из них имеет свою реализацию класса do_something()?

29.04.2017

Ответы:


1

Небольшая вариация ответа мельпомены (добавление базовой структуры без шаблона, BaseOfBase, для структур Base<T>) позволяет использовать общий вектор базы производных классов различных типов T.

Рабочий пример

#include <vector>
#include <iostream>

struct BaseOfBase
 { virtual void do_something () = 0; };

template <typename T>
struct Base : public BaseOfBase
 {
   T val;

   void do_something ()
    { std::cout << "Base::do_something() [" << val << "]\n"; };
 };

template <typename T>
struct Derived : public Base<T>
 { void do_something()
    { std::cout << "Derived::do_something() [" << this->val << "]\n"; } };

int main ()
 {
   std::vector<BaseOfBase*> vpbb;

   Base<int>            bi;
   Derived<int>         di;
   Base<std::string>    bs;
   Derived<std::string> ds;

   bi.val = 1;
   di.val = 2;
   bs.val = "foo";
   ds.val = "bar";

   vpbb.push_back(&bi);
   vpbb.push_back(&di);
   vpbb.push_back(&bs);
   vpbb.push_back(&ds);

   for ( auto const & pbb : vpbb )
      pbb->do_something();
 }
30.04.2017
  • ОП сказал: Я не хочу использовать стирание типов, или полиморфизм. 30.04.2017
  • @skypjack - да, но я также знаю, что виртуализация не работает в шаблонах классов 30.04.2017
  • Справедливо. ;-) 01.05.2017

  • 2

    Я сделал это, и, кажется, работает нормально:

    #include <iostream>
    
    template <typename T>
    struct Base {
        virtual void do_something() { std::cout << "Base::do_something()\n"; }
    };
    
    template <typename T>
    struct Derived : public Base<T> {
        virtual void do_something() { std::cout << "Derived::do_something()\n"; }
    };
    
    int main() {
        Base<int> b;
        Derived<int> d;
        Base<int> *p;
        p = &b;
        p->do_something();
        p = &d;
        p->do_something();
        return 0;
    }
    

    Выход:

    Base::do_something()
    Derived::do_something()
    
    29.04.2017

    3

    но я хочу хранить кучу производных классов и базовых классов в векторе (я не хочу использовать стирание типов или полиморфизм),

    Это уже просто невозможно в C++. В C++ вектор может содержать объекты только одного статического типа. Единственный способ, которым вектор может содержать разные типы объектов, - это если их статический тип все тот же, но они имеют разные динамические типы, но это стирание/полиморфизм типов, которые вы сказали, что не хотите использовать.

    Я думаю, возможно, вам нужно переосмыслить свои требования, потому что ваш вопрос по сути звучит так: я хочу что-то сделать, но я не хочу использовать технику X, которая явно определена как единственный способ сделать это что-то на С++!

    29.04.2017

    4

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

    @melpomene показал пример переопределения в целом, и я покажу здесь со специализацией:

    #include <iostream>
    
    template <typename T>
    class Base {
        public:
            virtual T do_something(T in) { std::cout << "Base::do_something()\n"; return in; }
    };
    
    
    class Derived : public Base<int> {
        public:
            virtual int do_something(int in) { std::cout << "Derived::do_something()\n"; return in - 1; }
    };
    
    void main()
    {
        Base<int> b;
        Derived d;
        Base<int> *p = &b;
        auto r1 = p->do_something(10);
        std::cout << r1 <<std::endl;
        p = &d;
        auto r2 = p->do_something(10);
        std::cout << r2 << std::endl;        
    }
    

    Что будет выводить

    Base::do_something()                                                                                                        
    10                                                                                                                          
    Derived::do_something()                                                                                                     
    9 
    

    Показывая, что он отлично работает, как и ожидалось.

    Что мы имеем в виду, когда говорим, что

    виртуализация не работает в шаблонах классов

    В основном означает, что вы не можете использовать в качестве шаблона производный класс, когда ожидается база.

    Рассмотрим приведенные выше классы Base<T> и Derived, тогда если у нас есть следующий код:

    #include <memory>
    
    template <typename T>
    void Test(std::unique_ptr<Base<T>> in){ std::cout << "This will not work with derived"; }
    
    void main()
    {
        Base<int> b;
        Derived d; 
    
        auto ptr = std::unique_ptr<Derived>(&d);
        Test(ptr); // <-- Will fail to compile as an invalid argument
    }
    

    он потерпит неудачу, потому что std::unique_ptr<Derived> не наследуется от std::unique_ptr<Base<T>>, хотя сам Derived наследуется от Base<T>.

    04.06.2020
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]