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