Templating in C++ is a mechanism that allows writing generic code, that is, code that can work with different types of data without having to rewrite it for each one.
Instead of defining a specific function or class for a data type (for example, int, double, string, etc.), we define a generic version that works for different types.
It is called this way because it is like making a “template” for functions or classes.
Templating does not imply a loss of efficiency, as the work of templating occurs during compilation, not during execution.
The compiler generates specific code for each type that you use with the template (this process is called template instantiation), generating customized versions of the code for the types needed.
Function Templates
Function templates allow you to define generic functions that can work with different types of data.
The basic syntax of a function template is:
template <typename T>
T genericFunction(T a, T b) {
// Function code
}
Instead of T
, you could use any other name, but in many cases, this name is used.
Let’s see it with an example.
#include <iostream>
template <typename T>
T sum(T a, T b) {
return a + b;
}
int main() {
std::cout << "Sum integers: " << sum(3, 4) << std::endl;
std::cout << "Sum floats: " << sum(3.5, 4.5) << std::endl;
return 0;
}
In this example, the function sum
can work with both integers and floats because we have defined it as a template.
Internally, the compiler generates a function for int
and another for double
, ensuring that each one is as efficient as if you had written them manually.
Class Templates
In addition to functions, it is also possible to use class templates, which allow us to define generic classes that can handle different types of data.
The basic syntax of a class template is very similar,
template <typename T>
class GenericClass {
private:
T data;
public:
GenericClass(T data) : data(data) {}
T getData() { return data; }
};
Let’s see it with an example,
#include <iostream>
template <typename T>
class Box {
private:
T content;
public:
Box(T content) : content(content) {}
T getContent() { return content; }
};
int main() {
Box<int> intBox(123);
Box<std::string> stringBox("Hello, C++");
std::cout << "Content of intBox: " << intBox.getContent() << std::endl;
std::cout << "Content of stringBox: " << stringBox.getContent() << std::endl;
return 0;
}
In this example, the class Box
can contain both integers and strings, or anything else (as a good box should).
Multiple Parameter Template
It is also possible to define templates that accept more than one type parameter.
template <typename T, typename U>
class Pair {
private:
T first;
U second;
public:
Pair(T first, U second) : first(first), second(second) {}
T getFirst() { return first; }
U getSecond() { return second; }
};
int main() {
Pair<int, std::string> pair(1, "One");
std::cout << "First: " << pair.getFirst() << ", Second: " << pair.getSecond() << std::endl;
return 0;
}
Template Specialization
Template specialization allows defining specific implementations for certain data types.
#include <iostream>
template <typename T>
class Printer {
public:
void print(T data) {
std::cout << "Data: " << data << std::endl;
}
};
// Specialization for type char*
template <>
class Printer<char*> {
public:
void print(char* data) {
std::cout << "String: " << data << std::endl;
}
};
int main() {
Printer<int> intPrinter;
intPrinter.print(42);
Printer<char*> stringPrinter;
stringPrinter.print("Hello, specialization!");
return 0;
}
In this example, the Printer
class is specialized for the type char*
, allowing for a specific implementation for strings.
Template Type Deduction
Template type deduction is the process by which the C++ compiler automatically determines the type of template parameters based on the arguments we pass.
template <typename T>
T sum(T a, T b) {
return a + b;
}
int main() {
int x = 5, y = 10;
double p = 2.5, q = 3.5;
// The compiler automatically deduces the type of T
std::cout << sum(x, y) << std::endl; // Deduced: T = int
std::cout << sum(p, q) << std::endl; // Deduced: T = double
}
In this example:
- When you call
sum(x, y)
, the compiler sees thatx
andy
are of typeint
, so it deduces thatT = int
. - When you call
sum(p, q)
, it deduces thatT = double
.
This eliminates the need to explicitly write sum<int>
or sum<double>
.
Deduction Guides
Since C++17, we can also “help” the compiler deduce types for class templates using deduction guides.
template <typename T>
class Box {
public:
T content;
Box(T c) : content(c) {}
};
// Deduction guide
Box(const char*) -> Box<std::string>;
int main() {
Box c1(42); // Deduced: T = int
Box c2("Hello"); // Deduced: T = std::string (thanks to the deduction guide)
}
This is useful when a class constructor does not always clearly indicate the type of the template.