I was writing a short snippet of code to figure out how I would store different template specializations into one data structure (e.g. vector). I'm aware of tuple but that's not good because I want to be able to append specializations after the tuple was constructed.
Below is a snippet of code that I came up with. In short, my idea was to have every template specialization inherit from a common class Element and then store such instances into a vector<Element>. That worked, but now when I access that data from vector, I access it as an Element. I need some way to find out which Element is paired with which template specialization.
Using typeid directly on elements from the vector returns the base type. I tried to work around that by having a member function runtime_type return that information from the subclass. Unfortunately, it's not working because the member function from SparseSet<T> is not overriding the virtual member function from the base class. I'm not sure why.
#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;
}
When I run the snippet above, using the online compiler coliru (g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out), I get the following output.
typeid(element) = Called base 7Element
typeid(T) = 9SparseSetIiE
typeid(element) = Called base 7Element
typeid(T) = 9SparseSetIiE
0
This means that
elementsis avectorof instances of the classElement, period. When avectorallocates memory, for example, it allocates contiguous space leavingsizeof(Element)for each instance. How can that be used to hold an instance ofSparseSet?You may want to use a
std::vector<std::unique_ptr<Element>>, since aunique_ptr<Element>can hold a pointer to aSparseSet.