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;
}
  • someType can be replaced by any name. It is common to use T.

  • 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).


1. C++17 doesn’t force to this constraint anymore as long as the type can be deduced by the compiler