PROWAREtech
C++: Template Class Tutorial
Templates are super simple and are a great way to reuse code.
First, two classes that do not use templates and do the same thing. These will be converted to use one class template.
#include <iostream>
#include <string>
using namespace std;
class Proware
{
private:
int data;
/* make this constructor private so that the class can not
be initialized without passing the constructor an argument */
Proware() {}
public:
Proware(const int &argument) : data(argument) {}
/* this method will double the value stored in the class
and return it */
int Double()
{
return data + data;
}
};
int main()
{
Proware obj(123);
cout << obj.Double() << endl;
return 0;
}
#include <iostream>
#include <string>
using namespace std;
class Proware
{
private:
double data;
Proware() {}
public:
Proware(const double &argument) : data(argument) {}
int Double()
{
return data + data;
}
};
int main()
{
Proware obj(123.123);
cout << obj.Double() << endl;
return 0;
}
Now, a small, over simplified example of how template classes work. To convert these classes, the class declaration is proceeded by template <class TGenericType>
and then variables are declared as the generic type TGenericType
.
#include <iostream>
#include <string>
using namespace std;
template <class TGenericType> class Proware
{
private:
TGenericType data;
Proware() {}
public:
Proware(const TGenericType &argument) : data(argument) {}
TGenericType Double()
{
return data + data;
}
};
int main()
{
Proware<int> obj(123);
cout << obj.Double() << endl;
return 0;
}
The method Double
is defined inline. If defining a method (or member function) outside the class then it must be preceded with template <TGenericType>
. The following syntax is also valid:
/* notice three TGenericTypes */
template <class TGenericType> TGenericType Proware<TGenericType>::Double()
{
return data + data;
}
Here is this small example converted to a template class. It handles three datatypes with one class declaration.
#include <iostream>
#include <string>
using namespace std;
template <class TGenericType> class Proware
{
private:
TGenericType data;
Proware() {}
public:
Proware(const TGenericType &argument);
TGenericType Double();
};
template <class TGenericType> Proware<TGenericType>::Proware(const TGenericType &argument) : data(argument) {}
template <class TGenericType> TGenericType Proware<TGenericType>::Double()
{
return data + data;
}
/* now the driver code; notice that the same class is
working with three datatypes (int, string and double) */
int main()
{
// specify that the int datatype will be used
Proware<int> obj_int(8);
// specify that the string datatype will be used
Proware<string> obj_string("Test");
// specify that the float datatype will be used
Proware<float> obj_float(8.8);
/* double each of the values, notice how the string
class is contatenating the values instead of adding */
cout << obj_int.Double() << endl;
cout << obj_string.Double() << endl;
cout << obj_float.Double() << endl;
}
Template Specialization
Define a different implementation for a template (and not reuse code) with specialization. For example:
#include <iostream>
#include <string>
using namespace std;
template <class TGenericType> class Proware
{
private:
TGenericType data;
Proware() {}
public:
Proware(const TGenericType &argument) : data(argument) {}
TGenericType Double()
{
return data + data;
}
};
template<> class Proware <string>
{
private:
string data;
Proware() {}
public:
Proware(const string &argument) : data(argument) {}
const string &Data() const
{
return data;
}
int Find(const string query) const
{
size_t found = data.find(query, 0);
if (found == string::npos)
return -1;
return found;
}
};
int main()
{
Proware<string> obj_str("How now brown cow.");
Proware<double> obj_dbl(1234.5678);
Proware<int> obj_int(336699);
cout << obj_str.Data() << endl;
cout << "brown found at " << obj_str.Find("brown") << endl;
cout << obj_dbl.Double() << endl;
cout << obj_int.Double() << endl;
}
Templates can also have regular, as opposed to generic, typed parameters:
#include <iostream>
using namespace std;
template <class TGenericType, int number> class Proware
{
TGenericType array[number];
public:
void SetArrayItem(int index, TGenericType value);
TGenericType GetArrayItem(int index);
};
template <class TGenericType, int index> void Proware<TGenericType, index>::SetArrayItem(int index, TGenericType value)
{
array[index] = value;
}
template <class TGenericType, int index> TGenericType Proware<TGenericType, index>::GetArrayItem(int index)
{
return array[index];
}
int main() {
Proware<float, 10> floats;
Proware<int, 10> ints;
floats.SetArrayItem(2, 123.45);
ints.SetArrayItem(5, 12345);
cout << floats.GetArrayItem(2) << endl;
cout << ints.GetArrayItem(5) << endl;
return 0;
}
Template Functions
Functions can also have templates. This allows them to work with template classes but they stand on their own also.
template <typename TGenericType> TGenericType DoubleIt(TGenericType &data)
{
return data + data;
}