6.1 Templates

C++ for Generic and Reusable Code
Section titled “C++ for Generic and Reusable Code”This section delves further into the utilisation of C++ standard libraries, providing a more comprehensive understanding of their role in software development. We explore key aspects, including a detailed examination of templates and their powerful capabilities in creating generic and reusable code. Furthermore, we will undertake an in-depth discussion of the Standard Template Library (STL), a cornerstone of modern C++ programming. This will involve dissecting its core components (containers, iterators, and algorithms) and illustrating their practical application through examples. By the end of this section, you will have a solid grasp of how to effectively leverage the C++ standard libraries to enhance the efficiency, robustness, and maintainability of your C++ programs.
Templates
Section titled “Templates”Templates were a ground-breaking addition to C++, introduced with the C++98 standard. The C++98 standard, officially known as ISO/IEC 14882:1998, was released and published on September 1, 1998. It became widely available and adopted by compiler vendors and the programming community shortly thereafter, through the late 1990s and early 2000s. The purpose of their inclusion was to enable generic programming, a paradigm that allows algorithms and data structures to be written once, independently of the specific data types they operate on. Before templates, if you needed a function to sort integers, then floats, then strings, you’d have to write three separate (though structurally identical) functions, leading to significant code duplication and maintenance headaches.
They work by allowing you to write code with placeholder types (type parameters). The compiler then uses these type parameters to generate specific code at compile-time for each data type you use the template with. For example, a std::vector<int> and std::vector<double> are two distinct classes generated by the compiler from a single std::vector template definition. This “compile-time polymorphism” ensures type safety without the overhead of runtime type checking, making it highly efficient for creating flexible and reusable libraries like the C++ Standard Library (STL).
This will become clear shortly with more description, but first we need to look at templates before we examine how they are used in the C++ Standard Template Library (STL).
What are templates?
Section titled “What are templates?”C++ templates enable generic programming: you can define a single function (or class) that works with many data types, instead of writing and maintaining a separate overload for each one. When you call the template, the compiler automatically generates a type-specific instance for you.
Historically, C++ focused on function templates and class templates. However, modern C++ (C++11/14) also supports alias templates (for creating type aliases) and variable templates (for defining constants with different types). We’ll look at function templates first.
Function Templates
Section titled “Function Templates”There are many situations where we may need to write the same function for different data types. A simple example is adding two variables, which might be of type int, float, or double. In each case, the function should return the sum with the correct return type based on the input types.
If we write a separate function for each data type, we quickly end up with four or five nearly identical functions, duplicating code unnecessarily. This conflicts with the principles we have discussed so far in this module, such as code reuse and maintainability.
C++ templates provide a clean solution to this problem. Using function templates, we only need to write a single generic function signature. The C++ compiler automatically generates the appropriate type-specific versions as needed. This greatly simplifies programming. Here is an example:
Example: Here is a simple example of an add() function. If we want to use add() for both int and float types, we would need to create two separate overloaded functions — one for each data type:
int add(int a, int b) { return a + b; } // without function templatesfloat add(float a, float b) { return a + b; } // without function templatesIf additional data types need to be supported, more overloaded functions would have to be written, resulting in repetitive and redundant code.
By using C++ function templates, we can simplify this task and eliminate the need for multiple function definitions. Instead, a single generic function template can handle all the required data types. Here is the function template version of the add() function:
template <class T> //The class keyword here means any //standard OR user-defined type T add(T a, T b) { //C++ function template example return a+b; }Now (as before), this example is somewhat simplistic, since the + operator works directly on most built-in types. However, it serves as a good starting point. C++ function templates are useful whenever the same operation needs to be performed on multiple data types.

Figure 1. Meetings! (Work Chronicles)
While templates can save significant development time, experience shows that extra care is needed when testing template code during development to ensure correct behaviour across all supported types.
It is important to note that templates differ from regular classes and functions: when a template is instantiated, the compiler must see the full template definition in order to generate the corresponding code. The common practice is to place the complete template definition in a header file, which is then included wherever the template is used. In general, templates should not be split into separate .h and .cpp files.
This leads to an (arguably) interesting side-effect: because class templates must reside entirely in header files, the source code remains visible, even when distributing the product to external or potentially untrusted clients.
Class Templates
Section titled “Class Templates”Class templates are often used to create type-safe containers. They allow us to define a class that operates on any user-defined type, making them ideal for advanced storage and data management tasks.
This capability is provided by C++ class templates (also known as parameterised types) and is part of a broader technique called generic programming.
Here is a simple example of a made-up class called Storage, which provides basic storage functionality for values of a user-defined type:
// First Template Example// by Derek Molloy - Template Example
#include<iostream>using namespace std;
template<class T>class Storage { T values[100]; // can store 100 values of type T (quite basic)public: T& operator [](int index) { return values[index]; }};
int main() { Storage<int> intArray; Storage<float> floatArray;
for (int i=0; i<10; i++) { intArray[i] = i * i; floatArray[i] = (float)i/2.1234 ; }
for (int i=0; i<10; i++) { cout << "[intArray value = " << intArray[i] << " ][floatArray value = " << floatArray[i] << "]" << endl; }}The full source code for this example is listed in classTemplates1.cpp
[intArray value = 0 ][floatArray value = 0][intArray value = 1 ][floatArray value = 0.470943][intArray value = 4 ][floatArray value = 0.941886][intArray value = 9 ][floatArray value = 1.41283][intArray value = 16 ][floatArray value = 1.88377][intArray value = 25 ][floatArray value = 2.35471][intArray value = 36 ][floatArray value = 2.82566][intArray value = 49 ][floatArray value = 3.2966][intArray value = 64 ][floatArray value = 3.76754][intArray value = 81 ][floatArray value = 4.23849]This first example is a little basic, as it is fixed to contain only 100 elements of the user-defined type. We can remove this hard coding, by rewriting the code to:
// Second Template Example// by Derek Molloy - More advanced template example.
#include<iostream>using namespace std;
template<class T, int size> // with new size option!class Storage{T values[size]; // can store size values of type T
public: T& operator [](int index){ return values[index]; }};
int main(){ Storage<int,10> intArray; Storage<float,20> floatArray;
for (int i=0; i<10; i++) { intArray[i] = i * i; floatArray[i] = (float)i/2.1234 ; }
for (int i=0; i<10; i++) { cout << "[intArray value = " << intArray[i] << " ][floatArray value = " << floatArray[i] << "]" << endl; }}The full source code for this example is listed in classTemplates2.cpp
That is not quite everything about templates — there are some additional important features to be aware of:
- Inheritance: Class templates can inherit from other class templates or from regular (non-template) classes, enabling powerful forms of code reuse and extension.
- Friend functions: Friend functions can be declared within templates to allow certain external functions access to the class’s private members, while still working with multiple types.
- Static members: Class templates can have static members; however, these static members are unique to each specialisation of the template. For example, if a class template is instantiated for both
intandfloat, there will be a separate static member for theintversion and another for thefloatversion.
Modern Template Constraints: Concepts (C++20)
Section titled “Modern Template Constraints: Concepts (C++20)”In older C++, templates would accept almost any type, and errors would only appear if the type didn’t support a required operation (like + or []). This often resulted in long, cryptic compiler errors.
C++20 introduced Concepts, which allow you to specify requirements on template parameters explicitly. This leads to clearer code and much more readable error messages. For example, we can require that our add function only works with types that are numeric:
#include <concepts>
template <typename T>requires std::integral<T> || std::floating_point<T>T add(T a, T b) { return a + b;}By using Concepts, you ensure that the template is used correctly, which is particularly important in complex edge systems where debugging runtime issues can be difficult.
© 2026 Derek Molloy, Dublin City University. All rights reserved.