I am working on a program that has to use std::vector
, while the types of elements are unknown. The std::vector
can hold different types of elements. Specifically, these types are enumerated from a template class. What I want to do is something like below,
#include <vector>
template <size_t N>
class Element{
int array[N];
};
int main(){
std::vector<Element> vec;
// do something
}
where, class Element
is the types of objects to be stored in the vector. It holds an array of an arbitrary size. The vector stores some Elements
objects with unknown paremter N
.
One solution I found is something like below, since the values of N
are known at compilation time.
std::vector<std::variant<Element<1>, Element<2>, Element<3> > vec;
But it would be tricky as I have to enumerate all of them, and the application changes a lot.
I know this simple example can be resolved through dynamic memory allocation, but this is just an example, the program is much more complex.
Is there a better solution for this?
CodePudding user response:
I could suggest two approaches for this.
Ditching Templates
You could ditch the template parameter in your Element
class and have it contain a std::vector
whose size is set upon construction and never changed. Something like this:
class Element {
public:
Element(std::size_t size) : m_vector(size) {};
private:
std::vector<int> m_vector;
};
And then you use the m_vector
just as if it was an array. No problems in your main()
here.
Using Inheritance and Polymorphism
I think you refer to this when you talk about dynamic allocation, but I'll include it anyways. If you want to use templates, you can have your class Element
inherit from a non-templated abstract class to have an interface of all the methods. For example:
class ElementBase {
public:
ElementBase() = default;
virtual ~ElementBase() = default;
virtual std::size_t arraySize() const = 0;
virtual const int *array() const = 0;
};
template<std::size_t N>
class Element : public ElementBase {
public:
Element() = default;
~Element() override = default;
std::size_t arraySize() const override {
return N;
}
const int *array() const override {
return m_array;
}
private:
int m_array[N];
};
With this approach you cannot have a std::vector<ElementBase>
in your main()
because the class is abstract, but you can have a std::vector<ElementBase*>
or something like std::vector<std::unique_ptr<ElementBase>>
if you are going to need dynamic allocation.