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;
}