Introduction: What are Generics in C++ and Why Are They Needed?
In the world of programming, there is often a need to write code that works equally well with different data types. For example, an array sorting function should be able to sort both integers and strings or user-defined objects. Without a mechanism for generic programming (generics), one would have to write separate functions for each type, leading to code duplication, errors, and increased maintenance complexity.
In C++, this problem is solved by templates — a powerful tool that allows you to create generic functions and classes. The concept of generics in C++ is implemented precisely through templates. They allow the programmer to define an algorithm or data structure without being tied to a specific type, and instead substitute the type at compile time.
The main advantages of using templates:
- Reduced code duplication — one template replaces many overloaded functions.
- Type safety — all type checks are performed at compile time.
- Performance — templates add no runtime overhead, as code is generated separately for each specific type.
- Flexibility — you can create complex generic constructs, including containers (STL) and algorithms.
In this article, we will thoroughly examine how to use templates in C++, starting with simple examples and ending with advanced techniques.
Basics of Working with Function Templates
The simplest way to get acquainted with generics is to write a template function. Suppose we need a function that finds the maximum of two values. Without templates, we would have to write overloads for int, double, std::string, etc. With templates, this is done once:
#include <iostream>#include <string>
// Function templatetemplate <typename T>T max(T a, T b) { return (a > b) ? a : b;}
int main() { std::cout << max(10, 20) << std::endl; // int std::cout << max(3.14, 2.71) << std::endl; // double std::cout << max("apple", "orange") << std::endl; // const char* (string comparison) return 0;}Here, the keyword template declares a template, and typename T (or class T) specifies the type parameter. The compiler automatically generates a version of the function for each type used. It is important to note that the types must support the > operator — otherwise a compilation error will occur.
You can also specify multiple type parameters:
template <typename T1, typename T2>void printPair(T1 a, T2 b) { std::cout << "(" << a << ", " << b << ")" << std::endl;}
// UsageprintPair(42, "Hello"); // (42, Hello)Class Templates: Creating Universal Containers
Class templates allow you to create generic data structures — this is the foundation of the Standard Template Library (STL). Let's look at a simple example of a template class for a dynamic array:
#include <iostream>#include <stdexcept>
template <typename T>class Array {private: T* data; size_t size;public: Array(size_t sz) : size(sz) { data = new T[size]; } ~Array() { delete[] data; } T& operator[](size_t index) { if (index >= size) { throw std::out_of_range("Index out of range"); } return data[index]; } size_t getSize() const { return size; }};
int main() { Array<int> intArray(5); // Array of integers Array<double> doubleArray(3); // Array of floating-point numbers