If I have a template class, that I want to instantiate with different data types:
template <typename T>
class A {
T value;
// ...
};
And I also want to use the objects of this class in a Standard Template Library container (say vector).
In my understanding creating a vector of A objects would not be accepted by the compiler, because A<int> and A<char> are actually different types and I can't put them in the same vector.
The workaround I found was creating a base class, a derived template class, and a vector of base class pointers.
class ABase {
// ...
};
template <typename T>
class ADerived : public ABase{
T value;
// ...
};
std::vector<BaseA*> mySuperVector;
I am starting to experiment with templates to gain a better understanding and I am wondering whether there are better solutions for this. My workaround above gives me also headache, because I am afraid that typecasting will be inevitable at some point.
Templates are a compile-time code generation construct. If you need an heterogeneous container of objects at compile-time, then you can use std::tuple:
std::tuple my_tuple{A<int>{}, A<char>{}, A<double>{}};
If you need an heterogeneous container of objects at run-time, you do need some sort of polymorphism. Using a base class with virtual methods is a valid option. If you know all the possible choice of types your object can be in advance, you can also use std::variant:
using my_a = std::variant<A<int>, A<char>, A<double>>;
std::vector<my_a> vec;
In this case, my_a can either be A<int>, A<char>, or A<double> at any given time. The active alternative can change at run-time.
Needing to typecast to the derived type is not related to the derived type being the instantiation of a class template, it's a design issue. Maybe you need virtual functions or multiple containers instead.
Otherwise, your solution is fine, but do switch to std::unique_ptr if the container is supposed to own the contained objects.
Related
I am somewhat new to templates and I don't understand how the compiler derives the templates for child classes when I inherit from the base class in a way where I set the templates.
I am creating a genetic algorithm base class for which I have written an abstract base class for the individuals of a population. I want to have a general definition so I use templates to define the fenotype and genotype:
template<typename T, typename S>
class individual {
public:
individual(S& fenotyp, T& genotyp) :
fenotype(fenotyp), genotype(genotyp) {}
...
S fenotype;
T genotype;
...
};
When the individuals are bitstrings, I have the following child class:
class bitstring_individual : public individual<boost::dynamic_bitset<>,
boost::dynamic_bitset<>> {
public:
using individual::individual;
...
};
Now I no longer have to work with template brackets anymore. Further down the line, I have a function that given a population std::vector<individual<T,S>>, returns the half with the highest fitness. This works on any type of individual so we can keep the definition general:
template<typename T, typename S>
std::vector<individual<T,S>> select_best_half(std::vector<individual<T,S>> parents,
std::vector<individual<T,S>> children) {
...
}
However, if I call this function I get error: no matching function for call to select_best_half(...) and the compiler says template argument deduction/substitution failed: and mismatched types ‘individual<T, S>’ and ‘bitstring_individual'.
In the definition of bitstring_individual we see that:
bitstring_individual : individual<boost::dynamic_bitset<>,boost::dynamic_bitset<>>
so why does the compiler not understand that the templates should be boost::dynamic_bitset<>? Can someone help me understand how the compiler tackles this inheritance and how can I fix it?
(using bitset = boost::dynamic_bitset)
Your bitstring_individual is not the same as individual<bitset, bitset>, and the compiler rightfully does not recognize them as such. One inherits the other, yes, but that does not make them interchangeable everywhere - in particular when used as template arguments.
In short: vectors (and other containers) of different (even polymorphically related) types are not covariant. Just like you cannot pass a std::vector<int> to a function expecting a std::vector<long> you cannot pass a std::vector<bitstring_individual> where a std::vector<individual<bitset, bitset>> is expected.
Note: Yes, they are different conversions, but the idea is the same.
Imagine that sizeof(individual<bitset, bitset>) = 32 and that bitstring_individual adds some members so that sizeof(bitstring_individual) = 48. If the compiler deduced T = S = bitset, then it would generate a method signature containing std::vector<individual<bitset, bitset>>, so a vector whose elements have size 32. But when you try to call it, you are passing a vector whose elements have size 48. Those vectors are not covariant, which would invariably lead to problems.
If you want your concrete individuals to have no other functionality than what the templated base class provides, just do this:
using bitstring_individual = individual<bitset, bitset>;
Otherwise, your vectors cannot store the individuals directly - you would have to use something like std::vector<std::shared_ptr<individual<T, S>>> (alternatively unique_ptr or ref instead of shared_ptr) for all population vectors.
Your question actually deals with covariance and contravariance of (certain) types in C++. Even if you were to "hard-code" your template parameters, i.e. have:
using i_bs_bs = individual<bitset, bitset>;
using std::vector;
class bitstring_individual : public i_bs_bs { ... };
vector<bsi> select_best_half(vector<i_bs_bs> parents, vector<i_bs_bs> children) {
...
}
You'd still get an error passing a vector<bitstring_individual> to select_best_half(). Why? Because, in C++ std::vector<T> is not a covariant type constructor.
To take the the example from the linked-to Wikipedia page, Suppose your inheriting classes were Animal (base class) and Cat (derived class). In C++, you can't add an Animal to a vector of Cats. All elements of that vector need to be Cats. Similarly, and as #MaxLanghof's answer explains, you can't add a bitstring_individual to a vector whose elements are of bitstring_individual's base type. Any special behavior necessary for handling bitstring_individual would simply not apply to the elements of a vector<i_bs_bs>.
Disclaimer: I am afraid the short answer to my question is "not possible". But since I am not a C++ expert, I thought I still give it a try and ask here, maybe there is some kind of solution which I am just not aware of.
So I have templated container-like class MyContainer that internally stores data in a std::list<T>, where T is the class template type. This works fine.
Now I want to add another class, which has to map instances of that templated container class in a std::map<std::string, MyContainer>. However, the compiler asks me to provide the template class type for the value-part of the map, as in std::map<std::string, MyContainer<T>>. But I would rather omit this template here, since that would in turn require me to use templates for the wrapper class as well.
Thus my question: Is there a way to achieve what I am trying to do, omitting the template type for the wrapper class, at least to some extend? Or is this just not possible in C++, because the compiler needs that information in any case?
One common technique for doing this is inheritance.
You make MyContainer<T> inherit from some base class, such as MyBaseContainer. MyBaseContainer is not a template class, but MyContainer is. The MyBaseContainer class has virtual functions that the templated class overrides.
Then, you make your map of type std::map<std::string, MyBaseContainer*>. It will be able to call the virtual functions on the containers it stores, without having to know the template type of each one.
This is how std::function works.
This is possible but a bit tricky. If i understand you correctly you want each MyContainer class stored inside map to possibly have different template specialization (say some will hold std::list<int> while other will hold std::list<string>) then you value type for map can no longer be just MyContainer but rather class that could hold both list<int> and list<string>.
One way to do it to use inheritance as #IanPudney already pointed out.
However you can do this even without creating inheritance hierarchy for MyContainer classes if you instead declare map as map<string, boost::any> (see http://www.boost.org/doc/libs/1_61_0/doc/html/boost/any.html) that way you will be able to store any kind of MyContainer inside this map without the need to create inheritance hierarchy in advance.
There are two possible ways to do this without using inheritance but this may also depend on a few deciding factors. The first question is: Upon creating an instance of the outside class is the type of the template container known. If it is then this is an easy class to write. If it is not then it can still be done but there would be more work involved. If case is not known there are 2 ways to do this and the first is the one you are trying to avoid by not making this a class template. The second involves more work which some of the logic on how to define this class will be shown.
Pseudo Code: -- 1st Case Where Type Is Known Upon Creation
#include <map>
template<class T>
class MyContainer {
// ... Class Variables, Constructors & Methods
}
// For Demonstration We will say that `T` is known to be an int upon instantiation
class MyClass {
private:
std::map< std::string, MyContainer<int> > maps_;
public:
MyClass() {}
~MyClass() {
// Clear Out Map
}
void addItem( std::string& str, int value ) {
maps_.insert( std::make_pair( str, MyContainer<int>( value ) );
}
};
Now for the 2nd case where the type is not known using the method without templating the wrapper you will need to know of all of the types that this class can support and you will need to create typedef of each of these.
Pseudo Code -- 2nd Case Where Type Is Not Known:
#include <map>
template<class T>
class MyContainer {
// ... Class Variables, Constructors & Methods
}
// For Demonstration We will say that `T` is unknown before instantiation
class MyClass {
public:
typedef MyContainer<int> INTS;
typedef MyContainer<float> FLOATS;
typedef MyContainer<double> DOUBLES;
// And Do This For Every Type This Class Will Support.
private:
std::map< std::string, INTS > mapInts_;
std::map< std::string, FLOATS > mapFloats_;
std::map< std::string, DOUBLES > mapDoubles_;
// And You Will Need A Container For Each Supporting Type
public:
MyClass() {}
// If You Have Constructors Other Than Default That Excepts Parameter Types
// You Will Need A Constructor For Each Supporting Type
~MyClass() {
// Clear Out All Maps
}
void addInts( std::string& str, MyClass::INTS );
void addFloats( std::string& str, MyClass::FLOATS );
void addDoubles( std::string& str, MyClass::DOUBLES );
// And You Will Need A Corresponding Function For Each Type This Class Supports.
};
I would like to have a std::hash_map that maps (for instance) regular std:strings to multiple different specializations of another template class.
This example is what I'm trying to achieve (it's wrong and doesn't compile, though):
template<typename T>
class Foo {
public:
Foo(T _value)
{
this-> value = _value;
}
private:
T value;
};
int main()
{
hash_map<string, Foo> various_foos;
various_foos["foo"] = Foo<int>(17);
various_foos["bar"] = Foo<double>(17.4);
}
The map can only store a single value type, so it can't directly store objects of different types; and different specialisations of a class template are different types.
Common solutions are:
Store pointers to a polymorphic base type, and access the real type via virtual functions or RTTI. You will need to be a bit careful about managing the objects themselves - either store smart pointers, or keep them in some other data structure(s).
Store a discriminated union type such as boost::variant or boost::any
You generally can't have an element in your hash that's of an incomplete type. Can you make a non-template base class that the others can inherit from?
The reason for this largely boils down to how the compiler will interpret your request. If it can't compute the size of your Foo structure, it can't create the internals for the hash_map.
I have got two classes.
The first class (A) is builded with an template.
template <class T>
class A
{
public:
T value;
};
The second class (B) should have an object of class A as member variable. Like this:
class B
{
public:
A<int> value;
};
But now i want to use any kind of template-class in class A. Not only int.
Apparent I can't declare a (member-)variable which contains any kind of a class.
So, I need something like this:
class B
{
public:
A<*> value;
};
Is there any (clean) solution for this problem?
-- Greeting from Germany, Bastian
You cannot have a single class B with "any" member object, because B has to be a well-defined class, and A<T> is a different type for different types T. You can either make B a template itself:
template <typename T>
class B
{
A<T> value;
};
or you can take a look at boost::any, which is type-erasing container for arbitrary types (but making use of it requires a certain amount of extra work). The any class only works for value types, though, it's not completely arbitrary.
The simplest solution would be to make all A variants ineherit from a common interface, even if it's empty :
class IA{}
template <class T>
class A : public IA
{
public:
T value;
};
class B
{
public:
IA* value;
};
Now, the associated costs:
interactions with value are limited to the IA interface;
if you try to cast to get the real type, that mean that you know the real type, so it's of no use and make A type a parameter of B becomes really easier to use.
there are runtime costs associated to runtime inheritance
Advantage :
it's easily understood by other developers
it naturally limit the types possible to some specific ones
it don't use boost (sometimes, you just can't)
So to do better there are other less simple solutions but that are simple enough to be used :
If you can use boost, boost::any, boost::variant and boost::mpl might be base of solutions.
Boost any can be used as a safe replacement to void*. The only problem with this is that you can have ANY type, like if the type was a template parameter of the B class.
Boost variant might be used successfully if you know all the types that A can be.
MPL might be helpful if you just want to set a list of possible types and make sure your members apply only to them. You can do a ton of things with MPL so it really depends on your exact needs.
You've got two choices, I think. The first is to parameterize your class over the type parameters of the instance variables:
template <class T> struct B
{
A<T> value;
};
The other option is to declare value as a void* pointer. (But that's probably not what you want).
yes, it's already been done. boost::any.
I think it helps to understand, that templated classes create an entirely new and seperate class for every type you use with it. For instance, Vector<int> and Vector<float> are as separate as the classes VectorInt and VectorFloat.
For class B, you are basically asking that the value variable either be A<int> or A<float>, which is the same as saying you want value to either be a "A_int" or "A_float". And to accomplish that you... well, use another template!
Situation
I have a template class TIppImage<T> for image of type T. I have singleton class CIppMemoryManager which can store a number of images of different size and type.
class CIppMemoryManager
{
public:
/// ... Singleton interface ...
template<class T> TIppImage<T>* GetImage(width, height);
private:
CIppMemoryManager();
~CIppMemoryManager();
std::map<IppDataType, void*> m_Containers;
};
IppDataType is enum, which values correspond to actual types. All management is done in template class TIppImageContainer<T>. And all specialization of this class is stored in m_Containers as a void*. It's not very good, but it is at least simple.
With this approach, I can simply implement template GetImage method like this:
template<class T> TIppImage<T>* CIppMemoryManager::GetImage(width, height)
{
return reinterpret_cast<TIppImageContainer<T>*>(m_Containers[
TIppTypeTraits<T>::ipp_data_type])->GetImage(width, height);
}
where I'm using traits class TIppTypeTraits<T> to obtain enum value from given type.
Problem
I cannot simply implement non-template methods like constructor. I need to explicitly handle all possible types:
CIppMemoryManager::CIppMemoryManager()
{
m_Containers[ipp8u] = new CIppImageContainer<Ipp8u>;
m_Containers[ipp8s] = new CIppImageContainer<Ipp8s>;
m_Containers[ipp16u] = new CIppImageContainer<Ipp16u>;
m_Containers[ipp16s] = new CIppImageContainer<Ipp16s>;
...
}
Worse, for destructor I also need to deal with void*:
CIppMemoryManager::~CIppMemoryManager()
{
delete reinterpret_cast<TIppImageContainer<Ipp8u>*>(m_Containers[ipp8u]);
delete reinterpret_cast<TIppImageContainer<Ipp8s>*>(m_Containers[ipp8s]);
delete reinterpret_cast<TIppImageContainer<Ipp16u>*>(m_Containers[ipp16u]);
delete reinterpret_cast<TIppImageContainer<Ipp16s>*>(m_Containers[ipp16s]);
...
}
So, the questions are:
a) Is there some way to iterate through collection of different types? Cannot use traits class here since function is non-template.
b) Is there some better way to store collection of containers - objects of different type? When they are just a different specialization of common template class, containers itself are pretty simple.
I think the class variant from the boost library (boost::variant) may help you. You can use visitors to execute the appropriate code depending on the type stored in a variant. A std::vector<boost::variant<T0, T1,...>> can store a list of objects of different types.
As your objects are similar, they may have the same size in memory, which is a good thing since boost::variant storage is stack-based (no heap allocation - this is faster).
What's wrong with polymorphic CIppImageContainer<T> (make them all share a common base class) and a smart pointer ?
Or some kind of boost::variant ?
boost::mpl::for_each is tailor-made for that job. Define a vector of types to operate on, a functor or lambda expression to do something, and you are done.
boost::variant is the most likely candidate but sometimes variantS become rather large as they require some extra storage and also have to deal with alignment. So maybe boost::any has advantages in some situations as well:
std::vector<std::pair< Type, boost::any > > data;
To comfortably iterate over such a container is harder (boost::transform_iterator cannot have more than one return type, so this wont work without some template trickery).