Vector that can have 3 different data types C++
I'm trying to make a vector in C++ that can store 3 different data types. I do not want to use the boost library. Something like:
vector<type1, type2, type3> vectorName;
Do I need to make a template? And if yes how would I do that?
EDIT: as of C++17, the standard library now includes the class template std::variant, which is quite similar to pre-existing solutions in boost. variant
is a type-safe alternative to unions that allows multiple types to be joined using an "or" relationship, e.g., an std::variant<type1, type2, typ3>
contains either a type1
OR a type2
OR a type3.
It can be composed with std::vector
to yield exactly what you've described:
std::vector<std::variant<type1, type2, type3>> vectorName;
However, std::variant
does introduce some restrictions. For example, it cannot hold reference or array types, and the underlying type (i.e. type1
or type2
) can only be accessed by template code. If std::variant
does not permit the specific behavior you need, keep reading for a more complicated but more versatile approach that also has the benefit of working in any version of C++.
ORIGINAL ANSWER:
The easiest way to store multiple types in the same vector is to make them subtypes of a parent class, wrapping your desired types in classes if they aren't classes already.
class Parent {
// Anything common to the types should be declared here, for instance:
void print() { // Make this virtual if you want subclasses to override it
std::cout << "Printing!";
}
virtual ~Parent(); //virtual destructor to ensure our subclasses are correctly deallocated
};
class Type1 : public Parent {
void type1method();
};
class Type2 : public Parent {
void type2Method();
};
class Type3 : public Parent {
void type3Method();
};
You can then create a vector of Parent
pointers that can store pointers to the child types:
std::vector<Parent*> vec;
vec.push_back(new Type1);
vec.push_back(new Type2);
vec.push_back(new Type3);
When accessing elements directly from the vector, you'll only be able to use members that belong to Parent
. For instance, you can write:
vec[0]->print();
But not:
vec[0]->type1Method();
As the element type has been declared as Parent*
and the Parent
type has no member named type1Method
.
If you need to access the subtype-specific members, you can convert the Parent
pointers to subtype pointers like so:
Parent *p = vec[0];
Type1 *t1 = nullptr;
Type2 *t2 = nullptr;
Type3 *t3 = nullptr;
if (t1 = dynamic_cast<Type1*>(p)) {
t1->type1Method();
}
else if (t2 = dynamic_cast<Type2*>(p)) {
t2->type2Method();
}
else if (t3 = dynamic_cast<Type3*>(p)) {
t3->type3Method();
}
Although it's generally considered a better idea to avoid this kind of explicit type-branching and instead rely on virtual methods.
Be sure to delete the pointers before removing them from the vector if you use dynamic allocation, as I did in the example above. Alternatively, use smart pointers (probably std::unique_ptr
) and let your memory take care of itself:
std::vector<std::unique_ptr<Parent>> vec;
I'm trying to make a vector in C++ that can store 3 different data types.
The answer here really depends on the particular use case:
If the objects are somehow connected and similar in some fashion - create a base class and derive all the classes from it, then make the vector store
unique_ptr
s to the parent class (see ApproachingDarknessFish s answer for details),If the objects are all of fundamental (so built-in) types - use an
union
that groups the types and define avector<yourUnionType>
,If the objects are of unknown type, but you're sure they share a similiar interface, create a base class, and derive a templated child class from it (
template <typename T> class container: public parent{};
), and create avector<unique_ptr<parent>>
like in the first case,If the objects are of types that for some reason cannot be connected (so for example the
vector
storesint
,std::string
, andyourType
), connect them via anunion
, like in 2. Or - even better...
...if you have time and want to learn something - look how boost::any
is implemented and try implementing it yourself, if you really don't want to use the library itself. It's not as hard as it may seem.
you could use std::any, store your objects in the vector as any and when you pull them out use type() == typeid(mytype)
https://en.cppreference.com/w/cpp/utility/any
This is only for C++17 onwards though.