Suppose I have a class template which have a member pData
, which is an AxB
array of arbitary type T
.
template <class T> class X{
public:
int A;
int B;
T** pData;
X(int a,int b);
~X();
void print(); //function which prints pData to screen
};
template<class T>X<T>::X(int a, int b){ //constructor
A = a;
B = b;
pData = new T*[A];
for(int i=0;i<A;i++)
pData[i]= new T[B];
//Fill pData with something of type T
}
int main(){
//...
std::cout<<"Give the primitive type of the array"<<std::endl;
std::cin>>type;
if(type=="int"){
X<int> XArray(a,b);
} else if(type=="char"){
X<char> Xarray(a,b);
} else {
std::cout<<"Not a valid primitive type!";
} // can be many more if statements.
Xarray.print() //this doesn't work, as Xarray is out of scope.
}
As the instance Xarray is constructed inside of an if statement, I cannot use it anywhere else. I tried to make a pointer before the if statements but as the type of the pointer is unknown at that point, I did not succeed.
What would be a proper way of dealing with this kind of a problem?
If you want to work with different arrays, whatever their type, templates alone cannot help you. Currently, there is exactly no relationship between
X<int>
andX<char>
.If you want to treat them as two subtypes of a common type, you will have to use inheritance (and dynamically allocated variables). For instance, all
X<T>
may inherit the same base class, sayPrintable
, and you can store the data in aunique_ptr<Printable>
:But this is probably not the best design.
A probably better solution would be, instead of trying to work outside of the if, to move all the work inside of the if. In your simple example, this could be done by duplicating the call to print, but this is not pretty nice either. But, going toward this idea, we can create a template function that does the job:
C++ is a statically typed language, meaning that you must know the type of objects at compile time. In this case you are basing the type of the object constructed on user input, so it's not possible to know the type at runtime.
The most common way to address this issue is to use dynamic polymorphism in which functions are invoked via a common interface using late binding. We accomplish this in C++ using virtual functions. For example:
This solves the out of scope issue and allows you to print using the dynamic type of the XArray variable. Virtual functions are the secret sauce that make this possible.
The problem here is that
X<int>
andx<char>
are completely unrelated types.The fact that they are both a result of the same templated class won't help here.
I can see several solutions, but those depends on what you really need.
You could, for instance make the
X<>
instances derive from a common non-templated base class that has theprint()
method (eventually as a pure virtual). But before you do that, be sure that it makes sense on a functional level: one should use inheritance because it makes sense, not solely because of technical constraints. And if you do that, you probably will want to have a virtual destructor as well.You could also bind and store a
std::function<void ()>
to the method you want to call, but ensure that the objects are still "alive" (they aren't in your current code: both theX<int>
andX<char>
are destroyed when they go out of scope, way before you actually callprint()
).A final solution would be to make some variant type that is compatible with both
X<int>
andX<char>
(boost::variant<> can help here). You could then write a visitor that implements theprint()
functionality for each type.Picking the last solution, it would become something like:
We actually lack the knowledge to give a definitive answer: if your classes are "entities", then you probably should go for inheritance. If they are more like "value classes", then the variant way might be more suited.
Rather than trying to fit the templates into
main
I would go the opposite way than the rest of the suggestions... move the code out ofmain
and into it's own (possibly templated) function that needs to deal with a single type: