Introduction: What are Generics (Templates) in C++ and Why Are They Needed?
In the world of C++ programming, one of the key concepts that allows writing flexible, reusable, and type-safe code is templates. They are often called "generics" by analogy with other languages such as Java or C#, although in C++ this concept is significantly more powerful and deeper.
Imagine you need to write a function to find the maximum element in an array. For integers (int) it's one function, for floating-point numbers (double) it's another, for strings (std::string) it's a third. Without templates, you would have to duplicate code, which leads to errors and complicates maintenance.
Generics (templates) solve this problem. They allow you to write code that works with any data type without sacrificing performance (unlike virtual functions or void*). Templates are the foundation of the C++ Standard Library (STL): containers (std::vector, std::map), algorithms (std::sort, std::find), and smart pointers (std::shared_ptr) — all of them are implemented using templates.
In this article, we will break down what templates are in C++, how to properly declare and use them, and also look at practical examples.
Main Part: How to Use Generics (Templates) in C++
1. Function Templates
The simplest way to start working with generics is to create a function template. Instead of writing several overloaded versions, you write one, parameterized by type.
Syntax:
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}The keyword template tells the compiler that this is a template. <typename T> declares the type parameter T. You can use class instead of typename — there is no difference.
Usage example:
#include <iostream>#include <string>
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}
int main() { std::cout << max(10, 20) << std::endl; // int: 20 std::cout << max(3.14, 2.71) << std::endl; // double: 3.14 std::cout << max(std::string("C++"), std::string("Python")) << std::endl; // string: Python return 0;}Important: The compiler generates a separate version of the function for each type used. This is called template instantiation. This achieves maximum performance — there is no overhead from virtual calls.
Multiple type parameters:
template <typename T1, typename T2>void printPair(T1 a, T2 b) { std::cout << "(" << a << ", " << b << ")" << std::endl;}// printPair(1, "hello"); // Outputs: (1, hello)2. Class Templates
Class templates allow you to create generalized data structures. A classic example is std::vector<T>. You can create your own template class, for example, a simple stack:
template <typename T>class Stack {private: T* data; size_t size; size_t capacity;public: Stack(size_t cap = 10) : size(0), capacity(cap) { data = new T[capacity]; } ~Stack() { delete[] data; }
void push(const T& value) { if (size < capacity) { data[size++] = value; } }
T pop() { if (size > 0) { return data[--size]; } throw std::out_of_range("Stack is empty"); }
bool isEmpty() const {