Почему виртуальная функция-член вызывается из базового класса, а не из подкласса

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

Ниже приведен фрагмент кода, который я придумал. Короче говоря, моя идея состояла в том, чтобы каждая специализация шаблона наследовалась от общего class Element а затем сохранить такие экземпляры в vector<Element>, Это сработало, но теперь, когда я получаю доступ к этим данным из vectorЯ получаю к нему доступ как Element, Мне нужен какой-то способ узнать, какой Element в паре с какой специализацией шаблона.

С помощью typeid непосредственно на элементы из vector возвращает базовый тип Я пытался обойти это, имея функцию-член runtime_type вернуть эту информацию из подкласса. К сожалению, это не работает, потому что функция-член из SparseSet<T> не переопределяет виртуальную функцию-член из базового класса. Я не уверен почему.

#include <vector>
#include <iostream>
#include <tuple>
#include <typeinfo>
#include <algorithm>

class Element
{
public:
    virtual const std::type_info& runtime_type()
    {
        std::cout << " Called base ";
        return typeid(*this);
    }

    virtual ~Element() = default;
};

template<class T>
class SparseSet : public Element
{
public:
    T param;

    const std::type_info& runtime_type() override
    {
        std::cout << " Called derived ";
        return typeid(*this);
    }
};

class Manager
{
public:
    std::vector<Element> elements;

    template<class T>
    bool has()
    {
        const auto it = std::find_if(elements.begin(), elements.end(), [](auto &element) {
            std::cout << "typeid(element) = " << element.runtime_type().name() << std::endl;
            std::cout << "typeid(T) = " << typeid(T).name() << std::endl;
            return typeid(element) == typeid(T); 
        });
        return it != elements.end();
    }
};

int main()
{   
    SparseSet<int> ss_int;
    ss_int.param = 3;

    SparseSet<char> ss_char;
    ss_char.param = 'a';

    Manager manager;

    manager.elements.push_back(ss_int);
    manager.elements.push_back(ss_char);

    std::cout << manager.has<SparseSet<int>>() << std::endl;

    return 0;
}

Когда я запускаю фрагмент выше, используя онлайн-компилятор coliru (g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out), Я получаю следующий вывод.

typeid(element) =  Called base 7Element
typeid(T) = 9SparseSetIiE
typeid(element) =  Called base 7Element
typeid(T) = 9SparseSetIiE
0

1 ответ

Решение
std::vector<Element> elements;

Это значит, что elements это vector экземпляров класса Element, период. Когда vector выделяет память, например, выделяет непрерывное пространство, оставляя sizeof(Element) для каждого экземпляра. Как это можно использовать для хранения экземпляра SparseSet?

Вы можете использовать std::vector<std::unique_ptr<Element>>, так как unique_ptr<Element> может содержать указатель на SparseSet,

Другие вопросы по тегам