C++ Templates
Lesson outline
Introduction to Templates
Templates in C++ are a core feature of generic programming that let functions and classes work with generic types. Instead of writing separate versions of code for each type, you write a single definition that the compiler instantiates for the specific types you use.
They are heavily used throughout the C++ standard library (often colloquially called the STL), for example in containers such as std::vector, std::list, and std::map.
Function Templates
Function templates allow defining one function for multiple data types. The actual type is deduced when the function is called. With a single type parameter, both arguments must deduce to the same type.
Syntax:
template <typename T>
T function_name(T param1, T param2) { /* ... */ }
Mixed-Type Deduction
When argument types differ, use two template parameters and let the compiler deduce the result type from the expression:
Class Templates
Class templates make it possible to write generic classes that can store and operate on any type.
Multiple Template Parameters
Templates can accept more than one type parameter, enabling flexible and reusable data structures. In C++17, class template argument deduction (CTAD) can sometimes infer the template arguments from constructor arguments.
Template Parameter Kinds
| Kind | Example | Notes |
|---|---|---|
| Type parameter | template<typename T> | typename and class are interchangeable in parameter lists. |
| Non-type parameter | template<std::size_t N> | Since C++20, many literal types are allowed (not just integrals); commonly used with std::array<T,N>. |
| Template template parameter | template<template<typename> class C> | Allows passing a template as a parameter (e.g., an allocator or container). |
Constrained Templates (C++20 Concepts)
Concepts let you express requirements on template arguments, producing clearer errors and preventing invalid instantiations.
Best Practices
- Place template definitions (not just declarations) in headers, or use explicit instantiation if separating.
- Prefer clear, descriptive parameter names (
TKey,TValue) when multiple types are involved. - Constrain templates (C++20 concepts or SFINAE) to express requirements early and improve diagnostics.
- Keep implementations readable; refactor complex meta-programming into helper types/functions.
- Be aware of ADL and conversions in overloaded function templates to avoid surprises.
Common Pitfalls
- Linker errors: Only declaring a template in a header but defining it in a .cpp will cause undefined references in users of the template.
- Type deduction surprises: Single-parameter function templates require both arguments to have the same deduced type; use mixed-type forms or explicit template arguments when needed.
- CTAD traps: String literals deduce to
const char*; wrap instd::stringif that's what you want.