cpp-como-usar-templates

What are templates and how to use them in C++

  • 5 min

Templating in C++ is a mechanism that allows you to write generic code, meaning code that can work with different data types without having to rewrite it for each one.

Instead of defining a function or a class specific to a data type (for example, int, double, string, etc.), we define a generic version that works for different types.

It’s called that because it’s like making a “template” for functions or classes.

Templating does not imply a loss of efficiency, since the work of templating happens during compilation, not during execution.

The compiler generates code specific to each type you use with the template (this process is called template instantiation), creating customized versions of the code for the types it needs.

Function Templates

Function templates allow you to define generic functions that can work with different data types.

The basic syntax of a function template is:

template <typename T>
T genericFunction(T a, T b) {
    // Function code
}
Copied!

Instead of T you could use any other name, but often 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;
}
Copied!

In this example, the sum function can work with both integers and floats, because we defined it as a template.

Internally, the compiler generates one function for int and another for double, ensuring each one is as efficient as if you had written them manually.

Class Templates

Besides functions, it’s also possible to use class templates, which allow us to define generic classes that can handle different data types.

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; }
};
Copied!

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;
}
Copied!

In this example, the Box class can contain both integers and text strings, or anything else (as a good box should).

Templates with Multiple Parameters

It’s 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;
}
Copied!

Template Specialization

Template specialization allows you to define 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;
}
Copied!

In this example, the Printer class is specialized for the char* type, allowing a specific implementation for text strings.

Template Deduction

Type deduction in templates is the process by which the C++ compiler automatically determines the type of a template’s parameters based on the arguments we pass to it.

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
}
Copied!

In this example:

  • When you call sum(x, y), the compiler sees that x and y are of type int, so it deduces that T = int.
  • When you call sum(p, q), it deduces that T = 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)
}
Copied!

This is useful when a class’s constructor doesn’t always clearly indicate the template type.