Introduction
Templates are powerful features of C++ which allows you to write generic programs. In simple terms, you can create a single function or a class to work with different data types using templates. The concept of templates can be used in two different ways:
- Function Templates
- Class Templates
Use function templates to write generic functions that can be used with arbitrary types. For example, one can write searching and sorting routines which can be used with any arbitrary type. The Standard Template Library generic algorithms have been implemented as function templates, and the containers have been implemented as class templates.
C++ adds two new keywords to support templates: ‘template’ and ‘typename’. The second keyword can always be replaced by keyword ‘class’. Templates are expanded at compiler time. This is like macros. The difference is, compiler does type checking before template expansion. Templates are very useful when implementing generic constructs like vectors, stacks, lists, queues which can be used with any arbitrary type. C++ templates provide a way to re-use source code as opposed to inheritance and composition which provide a way to re-use object code.
Function Templates
A function template works in a similar to a normal function, with one key difference. A single function template can work with different data types at once but, a single normal function can only work with one set of data types. Normally, if you need to perform identical operations on two or more types of data, you use function overloading to create two functions with the required function declaration. However, a better approach would be to use function templates because you can perform the same task writing less and maintainable code.
A function template starts with the keyword template followed by template parameter/s inside < > which is followed by function declaration.
template <class T> T someFunction(T arg){ ... }
In the above code, T is a template argument that accepts different data types (int, float), and class is a keyword. You can also use keyword typename instead of class in the above example. When, an argument of a data type is passed to someFunction( ), compiler generates a new version of someFunction() for the given data type. To use this function template use the format someFunction <type> (parameters)
for the function call.
// Function Template to find the largest number #include <iostream> using namespace std; // Template function template <class T> T Large(T n1, T n2) { return (n1 > n2) ? n1 : n2; } int main() { int i1 = 1, i2 = 6; float f1 = 4.9, f2 = 9.0; char c1 = 'A', c2 = 'a'; cout << Large<int>(i1, i2) <<" is larger." << endl; cout << Large<float>(f1, f2) <<" is larger." << endl; cout << Large<char>(c1, c2) << " has larger ASCII value."; return 0; }
In above example where the generic type T is used as a parameter for Large the compiler can find out automatically which data type has to instantiate without having to explicitly specify it within angle brackets. So we could have written instead Large(i1,i2)
. Since both i 1 and i2 are of type int, and the compiler can automatically find out that the template parameter can only be int.
Because template function includes only one template parameter (class T) and the function template itself accepts two parameters, both of this T type, we cannot call our function template with two objects of different types as arguments:
int i; long l; k = Large(i,l);
We can also define function templates that accept more than one type parameter, simply by specifying more template parameters between the angle brackets. For example:
template <class T, class U> T Large (T a, U b) { return (a<b?a:b); }
Class Templates
Like function templates, you can also create class templates for generic class operations. Sometimes, you need a class implementation that is same for all classes, only the data types used are different. Normally, you would need to create a different class for each data type OR create different member variables and functions within a single class. This will unnecessarily bloat your code base and will be hard to maintain, as a change is one class/function should be performed on all classes/functions. However, class templates make it easy to reuse the same code for all data types.
template <typename T> class className { ... .. ... public: T var; T someOperation(T arg); ... .. ... };
In the above declaration, T is the template argument which is a placeholder for the data type used. Inside the class body, a member variable var and a member function someOperation() are both of type T.
To create a class template object, you need to define the data type inside a < > when creation.
className<dataType> classObject;
Example
// class templates #include <iostream> using namespace std; template <class T> class mypair { T a, b; public: mypair (T first, T second) {a=first; b=second;} T getmax (); }; // First 'T' is the template parameter. The second T refers to the type returned by the function. // Third T (the one between angle brackets)specifies that function's template parameter is also the class template parameter. template <class T> T mypair<T>::getmax () { T retval; retval = a>b? a : b; return retval; } int main () { mypair <int> myobject (100, 75); cout << myobject.getmax(); return 0; }
Template specialization
If we want to define a different implementation for a template when a specific type is passed as template parameter, we can declare a specialization of that template. For example, let’s suppose that we have a very simple class called mycontainer that can store one element of any type and that it has just one member function called increase, which increases its value. But we find that when it stores an element of type char it would be more convenient to have a completely different implementation with a function member uppercase, so we decide to declare a class template specialization for that type:
// Template specialization // Class template: template <class T> class mycontainer { T element; public: mycontainer (T arg) {element=arg;} T increase () {return ++element;} }; // Class template specialization: template <> class mycontainer <char> { char element; public: mycontainer (char arg) {element=arg;} char uppercase () { if ((element>='a')&&(element<='z')) element+='A'-'a'; return element; } }; int main () { mycontainer<int> myint (7); mycontainer<char> mychar ('j'); cout << myint.increase() << endl; cout << mychar.uppercase() << endl; return 0; }
Non-type parameters for templates
Besides the template arguments that are preceded by the class or typename keywords , which represent types, templates can also have regular typed parameters, similar to those found in functions. It is also possible to set default values or types for class template parameters. As an example, have a look at this class template that is used to contain sequences of elements:
// Sequence template template <class T, int N> class mysequence { T memblock [N]; public: void setmember (int x, T value); T getmember (int x); }; template <class T, int N> void mysequence<T,N>::setmember (int x, T value) { memblock[x]=value; } template <class T, int N> T mysequence<T,N>::getmember (int x) { return memblock[x]; } int main () { mysequence <int,5> myints; mysequence <double,5> myfloats; myints.setmember (0,100); myfloats.setmember (3,3.1416); return 0; }
Default initialized template
// Default values or types for class template parameters template <class T=char, int N=10> class mysequence {..}; // Create objects using the default template parameters by declaring: mysequence<> myseq; // Which would be equivalent to: mysequence<char,10> myseq;