Templates
Templates are one of C++'s most powerful features, enabling generic programming - writing code that works with any data type without sacrificing type safety. Instead of writing separate functions or classes for each type (int, double, string, etc.), templates allow you to write one implementation that the compiler generates for each type you use. This eliminates code duplication while maintaining compile-time type checking and performance.
Introduction to Templates
Templates allow you to write generic code that works with different data types. They enable code reusability and type safety. C++ supports function templates and class templates. Templates are the foundation of the Standard Template Library (STL) and enable you to write highly reusable, type-safe code.
When you use a template, the compiler generates specific code for each type you instantiate it with. This happens at compile time, so there's no runtime overhead. Templates enable you to write algorithms and data structures once and use them with any type that meets the requirements.
Understanding templates is essential for modern C++ programming. They're used extensively in the STL, enable design patterns, and allow you to write code that's both generic and efficient. Templates are a key feature that sets C++ apart from many other programming languages.
1. Function Templates
Function templates define a family of functions that work with different types:
#include <iostream>
using namespace std;
// Function template
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
cout << maximum(5, 10) << endl; // int
cout << maximum(5.5, 3.2) << endl; // double
cout << maximum('a', 'z') << endl; // char
return 0;
}Multiple Template Parameters
#include <iostream>
using namespace std;
template <typename T, typename U>
void printPair(T a, U b) {
cout << a << " " << b << endl;
}
int main() {
printPair(5, 3.14); // int, double
printPair("Hello", 10); // string, int
return 0;
}2. Class Templates
Class templates allow you to define classes that work with different data types:
#include <iostream>
using namespace std;
template <class T>
class Stack {
private:
T* arr;
int top;
int capacity;
public:
Stack(int size) {
capacity = size;
arr = new T[capacity];
top = -1;
}
~Stack() {
delete[] arr;
}
void push(T element) {
if (top >= capacity - 1) {
cout << "Stack overflow!" << endl;
return;
}
arr[++top] = element;
}
T pop() {
if (top < 0) {
cout << "Stack underflow!" << endl;
return T();
}
return arr[top--];
}
T peek() {
if (top < 0) {
return T();
}
return arr[top];
}
};
int main() {
Stack<int> intStack(5);
intStack.push(10);
intStack.push(20);
cout << intStack.pop() << endl;
Stack<string> stringStack(5);
stringStack.push("Hello");
stringStack.push("World");
cout << stringStack.pop() << endl;
return 0;
}3. Template Specialization
Template specialization allows you to provide a specific implementation for a particular type:
#include <iostream>
using namespace std;
// General template
template <typename T>
void printType(T value) {
cout << "Generic: " << value << endl;
}
// Specialization for int
template <>
void printType<int>(int value) {
cout << "Integer: " << value << endl;
}
// Specialization for double
template <>
void printType<double>(double value) {
cout << "Double: " << value << endl;
}
int main() {
printType(10); // Uses int specialization
printType(3.14); // Uses double specialization
printType("Hello"); // Uses generic template
return 0;
}4. Non-type Template Parameters
#include <iostream>
using namespace std;
template <typename T, int size>
class Array {
private:
T arr[size];
public:
void set(int index, T value) {
if (index >= 0 && index < size) {
arr[index] = value;
}
}
T get(int index) {
if (index >= 0 && index < size) {
return arr[index];
}
return T();
}
int getSize() {
return size;
}
};
int main() {
Array<int, 5> intArr;
intArr.set(0, 10);
cout << intArr.get(0) << endl;
Array<double, 10> doubleArr;
doubleArr.set(0, 3.14);
cout << doubleArr.get(0) << endl;
return 0;
}5. Default Template Arguments
#include <iostream>
using namespace std;
template <typename T = int, int size = 10>
class Container {
private:
T arr[size];
public:
void display() {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
};
int main() {
Container<> c1; // Uses default: int, 10
Container<double> c2; // Uses default size: 10
Container<int, 5> c3; // Explicit: int, 5
return 0;
}6. Template Functions with Multiple Types
#include <iostream>
using namespace std;
template <typename T, typename U>
auto add(T a, U b) -> decltype(a + b) {
return a + b;
}
int main() {
cout << add(5, 3.14) << endl; // int + double
cout << add(10.5, 20) << endl; // double + int
return 0;
}7. Variadic Templates (C++11)
Templates that can accept a variable number of arguments:
#include <iostream>
using namespace std;
// Base case
void print() {
cout << endl;
}
// Variadic template
template <typename T, typename... Args>
void print(T first, Args... args) {
cout << first << " ";
print(args...); // Recursive call
}
int main() {
print(1, 2, 3, 4, 5);
print("Hello", "World", 10, 3.14);
return 0;
}8. Complete Example
#include <iostream>
using namespace std;
template <typename T>
class Vector {
private:
T* data;
int size;
int capacity;
public:
Vector() : size(0), capacity(1) {
data = new T[capacity];
}
~Vector() {
delete[] data;
}
void push_back(T value) {
if (size >= capacity) {
capacity *= 2;
T* newData = new T[capacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
delete[] data;
data = newData;
}
data[size++] = value;
}
T at(int index) {
if (index >= 0 && index < size) {
return data[index];
}
return T();
}
int getSize() {
return size;
}
};
int main() {
Vector<int> intVec;
intVec.push_back(10);
intVec.push_back(20);
cout << intVec.at(0) << endl;
Vector<string> stringVec;
stringVec.push_back("Hello");
stringVec.push_back("World");
cout << stringVec.at(0) << endl;
return 0;
}Summary
| Topic | Key Points | Difficulty |
|---|---|---|
| Function Templates | Generic functions using template<typename T>, compiler generates for each type | Intermediate |
| Class Templates | Generic classes, specify type when creating objects: Stack<int> s; | Intermediate |
| Template Specialization | Specific implementation for particular type, template<> syntax | Advanced |
| Non-type Parameters | Template parameters that are values not types: template<int size> | Advanced |
| Default Template Arguments | Provide default types/values: template<typename T = int> | Intermediate |
| Variadic Templates | Templates accepting variable arguments, typename... Args, C++11 feature | Advanced |
Frequently Asked Questions
Q1: What is the difference between typename and class in template declarations?
In template parameter declarations, typename and class are equivalent. Both can be used: template<typename T> ortemplate<class T>. However, typename is preferred for clarity (it can be any type, not just classes). In other contexts, typename has different meaning (dependent type names).
Q2: When does template instantiation happen?
Template instantiation happens at compile time. When you use a template with a specific type (e.g., max(5, 10)), the compiler generates the specific function for that type. Each unique type combination creates a new instantiation. This is why templates are fast (no runtime overhead) but can increase compile time and code size (code bloat) if many instantiations are created.
Q3: What is template specialization and when should I use it?
Template specialization provides a specific implementation for a particular type when the generic template isn't suitable. Use it when a type needs special handling. Syntax: template<> void func<int>(int x). Specialization allows optimization for specific types or handling edge cases. Full specialization provides implementation for one type, partial specialization (for classes) provides for a subset of types.
Q4: Can templates have default arguments?
Yes, templates support default arguments: template<typename T = int, int size = 10>. Default template arguments allow you to omit type/value when instantiating: Container<> c;uses defaults. Function templates got default arguments in C++11, class templates always had them. Defaults must be specified from right to left.
Q5: What are variadic templates and how do they work?
Variadic templates (C++11) accept variable number of template arguments: template<typename... Args>. They use parameter pack expansion and recursion. Base case handles empty pack, recursive case processes one argument and recurses with remaining. Used in std::make_tuple, printf-like functions, perfect forwarding. Enable type-safe variable argument functions.
Q6: What is SFINAE (Substitution Failure Is Not An Error)?
SFINAE means if template argument substitution fails, compiler doesn't error but tries next overload/specialization. This enables template metaprogramming, type traits, and conditional compilation. Used to enable/disable template functions based on type properties. Modern C++ (C++20) provides concepts which are cleaner than SFINAE for type constraints.
Q7: Can I use templates with inheritance?
Yes, templates work with inheritance. You can have template base classes, derived classes can be templates, and you can inherit from template instantiations. Template inheritance enables CRTP (Curiously Recurring Template Pattern) and mixins. Derived template classes can inherit from template base classes, and you can specialize base class templates.
Q8: What are template constraints and concepts (C++20)?
Concepts (C++20) specify requirements on template parameters: template<std::integral T>requires T to be an integral type. They provide better error messages, enable overloading based on concepts, and replace SFINAE in many cases. Concepts make templates more readable and provide compile-time type checking. They're the modern way to constrain templates.
Conclusion
Templates are a cornerstone of modern C++ programming, enabling generic code that works with any type while maintaining type safety and performance. Function templates eliminate code duplication for algorithms, while class templates enable generic data structures. The Standard Template Library is built entirely on templates, demonstrating their power and importance.
Template specialization allows you to optimize for specific types, while variadic templates enable functions with variable arguments. Non-type template parameters enable compile-time constants, and default template arguments provide convenience. Understanding these features enables you to write highly reusable, efficient code.
Mastery of templates is essential for advanced C++ programming. They enable design patterns, metaprogramming, and the creation of powerful libraries. Modern C++ features like concepts (C++20) make templates even more powerful and easier to use. Templates are what make C++ suitable for both high-level abstractions and low-level performance-critical code.
Related Links
🔹 Author: Dr. J. Siva Ramakrishna
🔹 Institution: Narayana Engineering College, Gudur
🔹 Last Updated: 9 January 2026