Templates in C++
Templates are one of the most powerful features in C++, allowing us to write generic code that can work with various data types. With templates, we can write a single function or a single class that can be used with int, float, string, or any other data type without having to rewrite the same code over and over.
This concept is known as generic programming. Instead of writing a max function for int, then a separate max function for float, and so on, just write one template function that can handle all of those data types. The compiler will automatically create a specific version of the function for each data type used.
C++ supports several kinds of templates: function templates, class templates, variable templates (C++14), and template specialization. Starting with C++20, there is also the concepts feature, which allows us to constrain the data types used in a template.
Code Example
#include <iostream>
#include <string>
using namespace std;
// Function template
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
// Variable template (C++14)
template <typename T>
const T pi = T(3.14159265358979323846);
// Class template
template <typename T>
class Stack {
private:
static const int MAX_SIZE = 100;
T data[MAX_SIZE];
int topIndex;
public:
Stack() : topIndex(-1) {}
void push(T value) {
if (topIndex >= MAX_SIZE - 1) {
cout << "Stack is full!" << endl;
return;
}
data[++topIndex] = value;
}
T pop() {
if (isEmpty()) {
cout << "Stack is empty!" << endl;
return T();
}
return data[topIndex--];
}
T top() const {
if (isEmpty()) {
cout << "Stack is empty!" << endl;
return T();
}
return data[topIndex];
}
bool isEmpty() const {
return topIndex == -1;
}
int size() const {
return topIndex + 1;
}
};
// Template specialization for the string type
template <>
class Stack<string> {
private:
static const int MAX_SIZE = 50;
string data[MAX_SIZE];
int topIndex;
public:
Stack() : topIndex(-1) {}
void push(string value) {
if (topIndex >= MAX_SIZE - 1) {
cout << "String stack is full!" << endl;
return;
}
data[++topIndex] = value;
}
string pop() {
if (isEmpty()) {
return "";
}
return data[topIndex--];
}
string top() const {
if (isEmpty()) {
return "";
}
return data[topIndex];
}
bool isEmpty() const {
return topIndex == -1;
}
int size() const {
return topIndex + 1;
}
};
// Function template with multiple type parameters
template <typename T, typename U>
void displayPair(T first, U second) {
cout << "Pair: (" << first << ", " << second << ")" << endl;
}
int main() {
// Function template
cout << "=== Function Template ===" << endl;
cout << "max(10, 20) = " << maximum(10, 20) << endl;
cout << "max(3.14, 2.71) = " << maximum(3.14, 2.71) << endl;
cout << "max('a', 'z') = " << maximum('a', 'z') << endl;
// Variable template
cout << "\n=== Variable Template ===" << endl;
cout << "pi (double) = " << pi<double> << endl;
cout << "pi (float) = " << pi<float> << endl;
// Class template with int
cout << "\n=== Class Template (int) ===" << endl;
Stack<int> intStack;
intStack.push(10);
intStack.push(20);
intStack.push(30);
cout << "Top: " << intStack.top() << endl;
cout << "Pop: " << intStack.pop() << endl;
cout << "Size: " << intStack.size() << endl;
// Class template with double
cout << "\n=== Class Template (double) ===" << endl;
Stack<double> doubleStack;
doubleStack.push(1.1);
doubleStack.push(2.2);
cout << "Top: " << doubleStack.top() << endl;
// Class template specialization for string
cout << "\n=== Class Template Specialization (string) ===" << endl;
Stack<string> stringStack;
stringStack.push("Hello");
stringStack.push("World");
cout << "Top: " << stringStack.top() << endl;
cout << "Pop: " << stringStack.pop() << endl;
// Multiple type parameters
cout << "\n=== Multiple Type Parameters ===" << endl;
displayPair(10, 3.14);
displayPair(string("Name"), string("John"));
return 0;
}
Code Explanation
-
template <typename T>— A template declaration.typenamecan be replaced withclass(both are the same).Tis the data type parameter that will be substituted when the template is used. -
maximum(T a, T b)— A function template that takes two parameters of the generic data typeT. The data typeTis determined automatically based on the arguments passed in. -
template <typename T> const T pi = T(3.14159...)— A variable template (C++14). Allows declaring generic variables that can be used with various data types. -
Stack<T>— A class template that creates a generic stack. All class members use the data typeT, so the stack can store any data type. -
template <> class Stack<string>— A template specialization. This is a special version ofStackthat applies only to thestringtype. Useful when a particular type requires different treatment. -
Stack<int> intStack— When using a class template, we must declare the data type to be used. This is called a template argument. -
template <typename T, typename U>— A template can have more than one data type parameter. ThedisplayPairfunction uses two different types at the same time. -
Template argument deduction — The compiler automatically determines the data type
Tbased on the arguments. For example,maximum(10, 20)automatically creates theintversion of the function.
Everyday Use Cases
Imagine you are building an online store application. You have many kinds of data that need to be sorted: product prices (double), stock quantities (int), product names (string). Without templates, you would have to write a separate sort function for each data type.
With templates, just write one generic sort function that can sort arrays of any data type. A template stack can be used for a shopping history system (storing recently viewed items), a template queue for an order queue, and so on.
// Sorting various data types with a single function template
template <typename T>
void bubbleSort(T arr[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
T temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// Can be used for int, double, string, and others
int price[] = {50000, 10000, 30000};
string product[] = {"Laptop", "Mouse", "Keyboard"};
bubbleSort(price, 3); // Sorts the prices
bubbleSort(product, 3); // Sorts the product names
Conclusion
Templates enable the writing of generic, flexible, and reusable code. Function templates create functions that can work with various data types, class templates create generic classes, and template specialization allows special treatment for specific types. With templates, code becomes more concise, easier to maintain, and avoids code duplication.