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
std::vector<Element> elements;
This means that elements
is a vector
of instances of the class Element
, period. When a vector
allocates memory, for example, it allocates contiguous space leaving sizeof(Element)
for each instance. How can that be used to hold an instance of SparseSet
?
You may want to use a std::vector<std::unique_ptr<Element>>
, since a unique_ptr<Element>
can hold a pointer to a SparseSet
.