Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ One std::vector containing template class of multiple types

I need to store multiple types of a template class in a single vector.

Eg, for:

template <typename T> class templateClass{      bool someFunction(); }; 

I need one vector that will store all of:

templateClass<int> t1; templateClass<char> t2; templateClass<std::string> t3; etc 

As far as I know this is not possible, if it is could someone say how?

If it isn't possible could someone explain how to make the following work?

As a work around I tried to use a base, non template class and inherit the template class from it.

 class templateInterface{      virtual bool someFunction() = 0;  };   template <typename T>  class templateClass : public templateInterface{      bool someFunction();  }; 

I then created a vector to store the base "templateInterface" class:

std::vector<templateInterface> v; templateClass<int> t; v.push_back(t); 

This produced the following error:

error: cannot allocate an object of abstract type 'templateInterface' note: because the following virtual functions are pure within 'templateInterface' note: virtual bool templateInterface::someFunction() 

To fix this error I made the function in templateInterface not a pure virtual by providing a function body, this compiled but when calling the function the overide is not used, but instead the body in the virtual function.

Eg:

 class templateInterface{      virtual bool someFunction() {return true;}  };   template <typename T>  class templateClass : public templateInterface{      bool someFunction() {return false;}  };   std::vector<templateInterface> v;  templateClass<int> i;  v.push_back(i);  v[0].someFunction(); //This returns true, and does not use the code in the 'templateClass' function body 

Is there any way to fix this so that the overridden function is used, or is there another workaround to store multiple template types in a single vector?

like image 583
jtedit Avatar asked May 13 '13 17:05

jtedit


People also ask

Can a vector have multiple types C++?

“No” C++ is a statically-typed language. A vector will hold an object of a single type, and only a single type.

Can a vector contain multiple data types?

Yes it is possible to hold two different types, you can create a vector of union types.

Which template can have multiple parameters in C++?

Function Templates with Multiple Parameters You can also use multiple parameters in your function template. The above syntax will accept any number of arguments of different types. Above, we used two generic types such as A and B in the template function.


2 Answers

Why your code doesn't work:

Calling a virtual function on a value doesn't use polymorphism. It calls the function which is defined for the type of this exact symbol as seen by the compiler, not the runtime type. When you insert sub types into a vector of the base type, your values will be converted into the base type ("type slicing"), which is not what you want. Calling functions on them will now call the function as defined for the base type, since not it is of that type.

How to fix this?

The same problem can be reproduced with this code snippet:

templateInterface x = templateClass<int>(); // Type slicing takes place! x.someFunction();  // -> templateInterface::someFunction() is called! 

Polymorphism only works on a pointer or reference type. It will then use the runtime type of the object behind the pointer / reference to decide which implementation to call (by using it's vtable).

Converting pointers is totally "safe" with regard to type slicing. Your actual values won't be converted at all and polymorphism will work as expected.

Example, analogous to the code snippet above:

templateInterface *x = new templateClass<int>();  // No type slicing takes place x->someFunction();  // -> templateClass<int>::someFunction() is called!  delete x;  // Don't forget to destroy your objects. 

What about vectors?

So you have to adopt these changes in your code. You can simply store pointers to actual types in the vector, instead of storing the values directly.

When working with pointers you also have to care about deleting your allocated objects. For this you can use smart pointers which care about deletion automatically. unique_ptr is one such smart pointer type. It deletes the pointee whenever it goes out of scope ("unique ownership" - the scope being the owner). Assuming the lifetime of your objects is bound to the scope this is what you should use:

std::vector<std::unique_ptr<templateInterface>> v;  templateClass<int> *i = new templateClass<int>();    // create new object v.push_back(std::unique_ptr<templateInterface>(i));  // put it in the vector  v.emplace_back(new templateClass<int>());   // "direct" alternative 

Then, call a virtual function on one of these elements with the following syntax:

v[0]->someFunction(); 

Make sure you make all functions virtual which should be possible to be overridden by subclasses. Otherwise their overridden version will not be called. But since you already introduced an "interface", I'm sure you are working with abstract functions.

Alternative approaches:

Alternative ways to do what you want is to use a variant type in the vector. There are some implementations of variant types, the Boost.Variant being a very popular one. This approach is especially nice if you don't have a type hierarchy (for example when you store primitive types). You would then use a vector type like std::vector<boost::variant<int, char, bool>>

like image 177
leemes Avatar answered Oct 04 '22 16:10

leemes


Polymorphism only works through pointers or references. You'll need the non-template base. Beyond that, you'll need to decide where the actual objects in container will live. If they're all static objects (with sufficient lifetime), just using a std::vector<TemplateInterface*>, and inserting with v.push_back(&t1);, etc., should do the trick. Otherwise, you'll probably want to support cloning, and keep clones in the vector: preferably with Boost pointer containers, but std::shared_ptr can be used as well.

like image 39
James Kanze Avatar answered Oct 04 '22 14:10

James Kanze