C++ templates enable generic programming. You write a function or class once, and the compiler generates specialized versions for each type you use it with. This avoids duplicating code for every type variation.
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int x = max(3, 5); // T is int
double y = max(3.14, 2.71); // T is double
The compiler sees max(3, 5) and generates a version of max for int. It sees max(3.14, 2.71) and generates a version for double. Same code, different types.
The Syntax: template<typename T>
The template<typename T> syntax declares a template parameter. T is a placeholder for any type. You can name it anything—T, Type, Element—but T is conventional for simple cases.
You can also write template<class T>. In this context, typename and class are interchangeable. They mean "T is a type parameter." The choice is stylistic. Modern C++ tends to prefer typename because it's clearer—T doesn't have to be a class, it can be any type.
template<typename T> // Preferred
T add(T a, T b) { return a + b; }
template<class T> // Equivalent
T add(T a, T b) { return a + b; }
Function Templates
Function templates generate functions for different types:
template<typename T>
void print(T value) {
std::cout << value << std::endl;
}
print(42); // Generates print<int>
print("hello"); // Generates print<const char*>
print(3.14); // Generates print<double>
The compiler deduces the type from the arguments. You can also specify it explicitly:
print<int>(42);
Explicit specification is sometimes necessary when the compiler can't deduce the type or when you want to force a specific instantiation.
Class Templates
Class templates work the same way but for entire classes:
template<typename T>
class Box {
public:
Box(T value) : data(value) {}
T get() const { return data; }
private:
T data;
};
Box<int> intBox(42);
Box<std::string> stringBox("hello");
Unlike function templates, class templates require explicit type specification. The compiler doesn't deduce it from constructor arguments (until C++17's class template argument deduction, which allows Box intBox(42); instead of Box<int> intBox(42);).
The standard library is full of class templates: std::vector<T>, std::map<K, V>, std::unique_ptr<T>. They're generic containers and utilities that work with any type.
Multiple Template Parameters
Templates can have more than one parameter:
template<typename K, typename V>
class Map {
// Key-value pairs
};
Map<std::string, int> ages;
Non-type parameters are also possible:
template<typename T, int Size>
class Array {
T data[Size];
};
Array<int, 10> arr; // Fixed-size array of 10 ints
Non-type parameters must be constants known at compile time.
When Templates Make Sense
Use templates when:
- You need the same logic for multiple types (containers, algorithms, smart pointers)
- Type safety matters—templates catch type errors at compile time
- Performance is critical—templates generate specialized code with no runtime overhead
Avoid templates when:
- The logic is type-specific and unlikely to generalize
- Compile times are already slow—templates increase compile time because the compiler generates code for each instantiation
- Error messages become unreadable—template errors can be verbose and cryptic
Template Specialization
Sometimes you need different behavior for specific types. Template specialization lets you provide a custom implementation:
template<typename T>
class Storage {
public:
void store(T value) { /* generic implementation */ }
};
// Specialization for bool
template<>
class Storage<bool> {
public:
void store(bool value) { /* specialized implementation */ }
};
Partial specialization is also possible for class templates with multiple parameters:
template<typename T, typename U>
class Pair { /* generic */ };
template<typename T>
class Pair<T, T> { /* both types the same */ };
Constraints and Concepts (C++20)
Templates accept any type by default. If your template assumes the type supports certain operations (like operator>), and you use it with a type that doesn't, you get a compile error—often deep in the template instantiation, not at the call site.
C++20 introduced concepts to constrain template parameters:
#include <concepts>
template<typename T>
requires std::integral<T>
T add(T a, T b) {
return a + b;
}
Now the compiler checks that T is an integral type before instantiating the template. If you try to call add(3.14, 2.71), you get a clear error at the call site, not buried in template code.
Concepts improve error messages and make template interfaces more explicit. If your codebase is on C++20 or later, they're worth using.
Common Pitfalls
Templates in headers: Template definitions usually need to be in header files. The compiler needs to see the full template definition to generate code for each instantiation. Separating template declarations and definitions into .h and .cpp files doesn't work the way it does for regular functions.
There's a workaround—explicit instantiation—but it's not common:
// In .cpp file
template class Box<int>; // Explicitly instantiate for int
Most projects just keep templates in headers.
Template bloat: Each instantiation generates code. If you instantiate a template with 10 different types, the compiler generates 10 versions of the code. This increases binary size and can slow down compilation.
If binary size matters, consider using non-template base classes for shared logic and templates only for type-specific parts.
Overly complex templates: Templates can do a lot—too much, sometimes. Template metaprogramming can solve compile-time problems, but it's hard to read and debug. Use it when simpler approaches don't work.
Templates in the Standard Library
Most of the C++ standard library is template-based:
- Containers:
std::vector<T>,std::map<K, V>,std::set<T> - Smart pointers:
std::unique_ptr<T>,std::shared_ptr<T> - Algorithms:
std::sort,std::find,std::transform
Understanding templates means understanding how to use the standard library effectively. It also means understanding error messages when something goes wrong, which is often the harder part.
Further Reading
The C++ reference on templates is comprehensive and includes examples of function templates, class templates, and specialization.
David Vandevoorde and Nicolai Josuttis wrote C++ Templates: The Complete Guide, which is the definitive book on the subject. It's thorough and occasionally dense, but if you're writing serious template code, it's the resource to consult.
For concepts and modern template constraints, see the C++ reference on constraints and concepts.
Templates are fundamental to C++ and to writing reusable, type-safe code. If you find yourself writing template<typename T> regularly and appreciate the power of generic programming, our C++ template tee is a minimal acknowledgment of that pattern.
0 comments