Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vector that can have 3 different data types C++

Tags:

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?

like image 581
Jonny Forney Avatar asked Oct 06 '14 01:10

Jonny Forney


2 Answers

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; 
like image 63
ApproachingDarknessFish Avatar answered Sep 20 '22 13:09

ApproachingDarknessFish


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:

  1. 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_ptrs to the parent class (see ApproachingDarknessFish s answer for details),

  2. If the objects are all of fundamental (so built-in) types - use an union that groups the types and define a vector<yourUnionType>,

  3. 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 a vector<unique_ptr<parent>> like in the first case,

  4. If the objects are of types that for some reason cannot be connected (so for example the vector stores int, std::string, and yourType), connect them via an union, 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.

like image 22
Paweł Stawarz Avatar answered Sep 23 '22 13:09

Paweł Stawarz