9/9
7. Template
Template corresponds to static genericity in C++ , i.e. generic functions whose type and implementation are fully known at compile time (no computational cost due to genericity).
Basic usage of template is to generate functions or objects handling generic types. Example of template use are STL containers. They can contains any variable type, but this type has to be known at compile time.
Function template
Let us suppose you want to create a function that displays all elements of any vector type (ex. std::array, or std::vector) and with arbitrary size.
The signature of such function could be, in principle, void display_all_elements( const someType& container ). To indicate to the compiler that someType must be understood as a generic template type, we indicate before the directive template <typename someType>.
The full template implementation is the following
#include <iostream>
#include <vector>
#include <array>
// Generic implementation of function displaying all elements of a vector-type
template <typename someType>
void display_all_elements( const someType& container )
{
// We suppose we can access elements using container[k] syntax
for(size_t k=0; k<container.size(); ++k)
std::cout << container[k] <<" ";
std::cout << std::endl;
}
int main()
{
// Declaration of two different types
std::array<float,2> v1 {1.4f, 2.5f};
std::vector<int> v2 {7, 8, 12, -1, 6, 3};
// Use of the same function
display_all_elements( v1 );
display_all_elements( v2 );
return 0;
}
-
someTypecan be replaced by any name. It is common to useT. -
The implementation with template class is similar to any implementation with explicitly defined type.
-
Functions with template arguments can be called like any function. The type passed as argument is automatically deducted by the compiler.
Variant of the function: you can make the function even more generic in using range-based loop instead of supposing that elements of the container can be accessed by container[k]. The function can then be called on any STL container
template <typename someType>
void display_all_elements( const someType& container )
{
// Valid for any iterator based container
for( const auto& element : container )
std::cout << element <<" ";
std::cout << std::endl;
}
Object template
Objects can also use template. Let suppose that you would like to write a generic vec3 that can contain any type as x, y, z (for instance int, float, double, long double, etc).
#include <iostream>
#include <vector>
#include <array>
// Generic vec3 containing arbitrary type
template <typename T>
struct vec3
{
T x;
T y;
T z;
};
int main()
{
// Declaring vec3 as containing various fundamental types
vec3<float> p1 {1.0f, 2.5f, 3.5f};
vec3<int> p2 {5, 4, 3};
vec3<double> p3 {5.7, 2.6, 1.1};
// Template are generic, each element of vec can be any type, for instance std::vector
vec3< std::vector<int> > p { {1,2,3,5}, {7,2}, {4,1,-2}};
p.y = {8,5,4,1};
std::cout<< p.y[2] << std::endl;
return 0;
}
-
Object template must explicitly indicate their type when declared [1].
General notes on templates
-
Template is a powerful mechanism from C++ allowing to handle generic types, and even perform computation at compile time. However, template syntax may become quickly complex. Don’t overuse template if not needed.
-
Compiler instantiate template code with the correct type and compile it everytime he meets a call. This can lead to long compilation time when using a lot of templated code.
-
Template doesn’t follow to separate compilation as explicit type (every template code must be compiled several time for each call with specific type). All template code must be defined in header file (or at least accessible in the file which is used).