Templates in C++¶
Template is a method in C++ language to generalize functions/classes to be used with various base types. It allows a same function or class to work with various data types.
E.g. STL containers like
vector<int>
,vector<string>
,set<int>
,map<int, double>
, etc. are templates.E.g. type cast functions like
static_cast<int>(3.5)
Similar idea to Java generics but with different implementation details
Pro and Con¶
Pro
Generalize functions/classes to work with various data types
Avoid code redundancy
Allow type-safe operations
Con
Code bloat
The compiler will generate a copy of the function/class for each base type used. This may cause the executable file to be very large.
Hard to debug
The error messages from the compiler may be hard to understand.
Compilation
The compilation can be very slow if the template is used in many places.
Header-only library
It violates the principle of separation of declaration and implementation.
Alternatives¶
There are other way to allow generalization of functions to work with different types.
function overloading
Define many functions like
min(int, int)
,min(double, double)
,min(char, char)
, with a same name but different parameter types.Similar logics may be repeated many times.
implicit type casting
A function
min(double, double)
can handle calls likemin(2, 3)
,min(2.0, 3)
,min('a', 66)
, etc.May cause confusion
Not work with complex data types
Sometimes the base types are not used as parameter types but internal variables. In these cases, template is the only solution.
Syntax¶
template
keywordtemplate <list of template parameters>
clause.The list of template parameters can contain one or more template parameters separated by commas.
The template parameters are enclosed in angle-brackets (<>).
We only focus on type template parameters, which is used to match a type.
Started with keyword
class
ortypename
(interchangeable)
The
template
clause must be above the following entities when every the parameterized type is used.function declaration
function definition
class declaration
method definition
1template<typename T> 2void func1(T param1); 3 4template<typename T> 5class MyClass{ 6 public: 7 MyClass(T param1); 8}; 9 10// important to notice that the first MyClass is followed by <T> here! 11// MyClass is the name of the template while MyClass<T> is the 12// name of the class 13template<typename T> 14MyClass<T>::MyClass(T param1) { 15 // core logics 16}
To use a template
Specify the template argument to instantiate the template:
1// function template 2int result = func1<int>(10); 3 4// class template 5MyClass<int> obj1; 6if (obj1.method1(10)) 7 cout << "True" << endl;
Template argument deduction:
1func1(10); // T will be int 2func1<>(10); // T will be int 3func1(10.0); // T will be double 4 5MyClass obj1(10); // T will be int
Note
Template argument deduction for class template not available until C++ 17
Split header/implementation file¶
TL;DR Put everything of a template in a single header file.
Single Header File¶
A template must be instantiated to provided the compilable source code. The
instantiation of templates is lazy. Only when a templated entity is used with
base type(s) specified, the template will be instantiated. For instance,
vector<int>
is an instantiation of the vector
template. When an
occurrence of vector<int>
is seen by the compiler, the compiler will search
to see if there is a version of it. If not, the compiler will look for the
source code of the vector
template and instantiate it to generate the
instance of vector<int>
.
a template itself cannot be compiled
a template must be instantiated to generate code to be compiled
instantiation is a process to provide really parameter to a template
when you instantiate a template in a cpp file and try to compile it, the compiler will look for the template to instantiate from the current cpp file, and all included header files, etc.
if the template code (not instantiated) is in another cpp file, the compiler cannot find it and the instantiation will fail.
Thus, it is natural to have a templated function/class in a single header file so that when a cpp file uses the template, the compiler can find all the template code in the header. This method is recommended in our courses.
Split To Multiple Files¶
Optional Contents Templates requires special organization of the source code because the compiler needs to see both the template definition (not instantiation) and its use within a single translation unit. Thus, it is not possible to split the template into a header file and a cpp file like normal modular code.
There are two work-around methods when the split is preferred:
split implementations in a separate text file and include it in the header file after the declarations. This method is not recommended but commonly seen in old-fashioned courses.
split implementations into a cpp file, explicitly declare all possible instantiations of the template in the cpp file; Thus, when other cpp needs to use the template, the instantiated codes have already been instantiated and ready for use.
Examples¶
1 template<typename T>
2 T func1(T param1);
3
4 template<typename T>
5 class MyClass{
6 public:
7 bool method1(T param1);
8 }
9
10 // important to notice that MyClass is followed by <T> here!
11 template<typename T>
12 bool MyClass<T>::method1(T param1) {
13 // logics
14 return true;
15 }
1#ifndef TEMPLATE_DEMO_HPP
2#define TEMPLATE_DEMO_HPP
3
4// function template
5template<typename T>
6T myMin(T, T);
7
8template<typename T>
9T myMin(T val1, T val2) {
10 if (val1 > val2)
11 return val2;
12 else
13 return val1;
14}
15
16// class template
17template<typename T>
18class Min {
19 private:
20 T val1;
21 T val2;
22 public:
23 Min(T val1, T val2);
24 T getMin();
25};
26
27// class methods implementations, not inline
28template<typename T>
29Min<T>::Min(T val1, T val2) {
30 this->val1 = val1;
31 this->val2 = val2;
32}
33
34template<typename T>
35T Min<T>::getMin() {
36 if (val1 > val2)
37 return val2;
38 else
39 return val1;
40}
41
42#endif
1#include "template-demo.hpp"
2#include <iostream>
3using namespace std;
4
5int main() {
6 // test myMin function, the function template
7 cout << "min(2, 3) = " << myMin(2, 3) << endl; // myMin(int, int)
8 cout << "min(2.0, 3.0) = " << myMin(2.0, 3.0) << endl; // myMin(double, double)
9 // myMin(2, 3.0) cannot compile because types much match
10 // Self-test: now to delare the function to make myMin(2, 3.0) work?
11
12 // test Min class, the class template
13 Min<int> myMin1(2, 3);
14 Min<double> myMin2(2.0, 3.0);
15
16 cout << "The min from the class Min<int>(2, 3) " << myMin1.getMin() << endl;
17 cout << "The min from the class Min<double>(2.0, 3.0) " << fixed
18 << myMin2.getMin() << endl;
19
20}