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

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() класса?

2
apramc 30 Апр 2017 в 02:04

4 ответа

Лучший ответ

Небольшое изменение ответа мельпомены (добавление базовой структуры без шаблона 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();
 }
2
max66 30 Апр 2017 в 00:29

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

#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()
3
melpomene 29 Апр 2017 в 23:16

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

@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>.

0
yoel halb 4 Июн 2020 в 23:31

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

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

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

2
Nir Friedman 29 Апр 2017 в 23:16