Templates allow us to write generic classes (called template classes) and generic functions(called template functions) that can work with generic data-types.
It is the responsibility of the compiler to create their specialized forms during the compilation step. Keywords template, typename and class are used to create templates in C++;
A template class deals with template parameters whereas a template function deals with both template parameters and function parameters.
/* Template Function. 'template <class T>' or 'template <typename T>' | |
can be used here.*/ | |
template <class T> | |
void exampleFunction(T val) { | |
} | |
/* Template Class. 'template <class T>' or 'template <typename T>' | |
can be used here.*/ | |
template <class T> | |
class ExampleClass { | |
}; | |
int main() { | |
exampleFunction<int>(5); //Here 'int' is a template argument and '5' is a function argument | |
ExampleClass<int> ec1;//Here 'int' is a template argument | |
return 0; | |
} |
In case of a template function with parameters, compiler can deduce the data-types based on the arguments passed to this function. But in case of a template function with no parameters as well as a template class, the data-types must be explicitly specified during function invocation and during the creation of the class instance respectively.
#include <iostream> | |
using namespace std; | |
/* Template Function. 'template <class T>' or 'template <typename T>' | |
can be used here.*/ | |
template <class T> | |
void exampleFunction(T val) { | |
cout << val; | |
} | |
/* Template Class. 'template <class T>' or 'template <typename T>' | |
can be used here.*/ | |
template <class T> | |
class ExampleClass { | |
T memberVariable; | |
public: | |
ExampleClass(T val) :memberVariable(val) {} | |
void printMemberVaraible() { | |
cout << memberVariable; | |
} | |
}; | |
int main() { | |
exampleFunction(5); //correct | |
exampleFunction<int>(5); //correct. 'int' type passed as template argument is optional; | |
ExampleClass ec1(4); //incorrect. 'int' Type needs to passed as a template argument. | |
ExampleClass<int> ec1(4);//correct | |
return 0; | |
} |
It is possible to pass type arguments (int,string etc) as well as non-type arguments (3,”hello” etc) as template parameters. Non-type arguments are usually passed in order to maintain state information. Non-type arguments must not be variables but constants as they need to be known at compile time.
#include <iostream> | |
using namespace std; | |
template <int min, int max> | |
class ExampleClass { | |
}; | |
int main() { | |
/*Incorrect usage of non-type parameters. They should be known at compile time.*/ | |
int min1, max1; | |
cin >> min1; cin >> max1; | |
ExampleClass<min1, max1> ec1; | |
/*Correct Usage non-type parameters.*/ | |
ExampleClass<0, 10> ec2; | |
return 0; | |
} |
Template Functions
Say we need to write a sort method that works with integer type, float type, char type, string type etc. We can write a separate sort() method for each of these types. But that is cumbersome and creates a lot of redundant code. It is in such scenarios that template functions come to our rescue. We can write a single sort() method that accepts a generic type as its argument. Now, its up to the compiler to create the specialized forms of this generic function if needed in the program.
#include <iostream> | |
#include <vector> | |
#include <string> | |
using namespace std; | |
template <typename T> | |
void sort(vector<T>& array) { | |
int i = 0; | |
while (i < array.size()) { | |
for (int j = 0; j < array.size() - 1-i; j++) { | |
if (array[j] > array[j+1]) { | |
T temp = array[j]; | |
array[j] = array[j + 1]; | |
array[j + 1] = temp; | |
} | |
} | |
i++; | |
} | |
} | |
int main() { | |
// int array | |
vector<int> intArray = {4,3,6,1}; | |
sort(intArray); | |
for (int each : intArray) cout << each << " "; | |
cout << endl; | |
// char array | |
vector<char> charArray = {'b','r','w','p'}; | |
sort(charArray); | |
for (char each : charArray) cout << each << " "; | |
cout << endl; | |
//string array | |
vector<string> stringArray = { "hello","how","are","you" }; | |
sort(stringArray); | |
for (string each : stringArray) cout << each << " "; | |
cout << endl; | |
cin.get(); | |
return 0; | |
} |
Output:
1 3 4 6 b p r w are hello how you
It is also possible to create our own specializations of a given template function. For example, consider the previous example. But there is a new requirement that if the input type is a string, sort() should perform sort in descending order. The rest of the requirement remain the same.
#include <iostream> | |
#include <vector> | |
#include <string> | |
using namespace std; | |
template <typename T> | |
void sort(vector<T>& array) { | |
int i = 0; | |
while (i < array.size()) { | |
for (int j = 0; j < array.size() - 1-i; j++) { | |
if (array[j] > array[j+1]) { | |
T temp = array[j]; | |
array[j] = array[j + 1]; | |
array[j + 1] = temp; | |
} | |
} | |
i++; | |
} | |
} | |
template <> | |
void sort (vector<string>& array) { | |
int i = 0; | |
while (i < array.size()) { | |
for (int j = 0; j < array.size() - 1 - i; j++) { | |
if (array[j+1] > array[j]) { | |
string temp = array[j]; | |
array[j] = array[j + 1]; | |
array[j + 1] = temp; | |
} | |
} | |
i++; | |
} | |
} | |
int main() { | |
// int array | |
vector<int> intArray = {4,3,6,1}; | |
sort(intArray); | |
for (int each : intArray) cout << each << " "; | |
cout << endl; | |
// char array | |
vector<char> charArray = {'b','r','w','p'}; | |
sort(charArray); | |
for (char each : charArray) cout << each << " "; | |
cout << endl; | |
//string array | |
vector<string> stringArray = { "hello","how","are","you" }; | |
sort(stringArray); | |
for (string each : stringArray) cout << each << " "; | |
cout << endl; | |
cin.get(); | |
return 0; | |
} |
Output:
1 3 4 6 b p r w you how hello are
Template Classes
In the example below, the first template parameter is a non-type parameter whereas the second template parameter is a type parameter. As you can see, it is also possible to provide default values to the template parameters.
#include <iostream> | |
using namespace std; | |
template <int val = 3, class T = int> | |
struct Example { | |
int multiplier = val; | |
T func(T a) { | |
a *= multiplier; | |
return a; | |
} | |
}; | |
int main() { | |
Example <> e1; | |
Example <5> e2; | |
Example <2, float> e3; | |
cout << e1.func(1) << endl; | |
cout << e2.func(4) << endl; | |
cout << e3.func(2.4) << endl; | |
cin.get(); | |
return 0; | |
} |
Output:
3 20 4.8
While creating a template class, we can either put the class definition and the implementation in a single header file(.h) or we can keep the class definition in a header file(.h) and the implementation in a separate .cpp file. But in the latter case, the .cpp file should also contain the following piece of code for each type that might be used with the template class.
template class class_name <type>; |
Variable Templates
C++14 has introduced a new type of templates called as variable templates. Please go through this article to learn more about them.
Leave a Reply