为什么从 base class 而不是 subclass 调用虚拟成员函数

Why is virtual member function being called from base class instead of a subclass

我正在编写一小段代码来弄清楚如何将不同的模板专业化存储到一个数据结构中(例如 vector)。我知道 tuple 但这不好,因为我希望能够在构建 tuple 之后附加专业化。

下面是我想出的一段代码。简而言之,我的想法是让每个模板特化都继承自一个公共 class Element,然后将此类实例存储到 vector<Element> 中。那行得通,但现在当我从 vector 访问该数据时,我将其作为 Element 访问。我需要一些方法来找出哪个 Element 与哪个模板专业化配对。

直接对来自 vector return 基本类型的元素使用 typeid。我试图通过一个成员函数 runtime_type return 来解决这个问题,该信息来自 subclass。不幸的是,它没有工作,因为来自 SparseSet<T> 的成员函数没有覆盖来自基础 class 的虚拟成员函数。我不确定为什么。

#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
std::vector<Element> elements;

这意味着 elements 是 class Element 句点的 vector 个实例。例如,当 vector 分配内存时,它会为每个实例分配连续的 space,留下 sizeof(Element)。如何使用它来保存 SparseSet 的实例?

您可能想要使用 std::vector<std::unique_ptr<Element>>,因为 unique_ptr<Element> 可以包含指向 SparseSet 的指针。