Introdução: O que são Generics (templates) em C++ e por que são necessários?
No mundo da programação em C++, um dos conceitos-chave que permite escrever código flexível, reutilizável e type-safe são os templates. Frequentemente são chamados de "generics" por analogia com outras linguagens, como Java ou C#, embora em C++ este conceito seja significativamente mais poderoso e profundo.
Imagine que você precisa escrever uma função para encontrar o elemento máximo em um array. Para números inteiros (int) seria uma função, para números de ponto flutuante (double) outra, para strings (std::string) uma terceira. Sem templates, você teria que duplicar código, o que leva a erros e dificulta a manutenção.
Generics (templates) resolvem esse problema. Eles permitem escrever código que funciona com qualquer tipo de dado, sem sacrificar a performance (ao contrário de funções virtuais ou void*). Templates são a base da biblioteca padrão do C++ (STL): containers (std::vector, std::map), algoritmos (std::sort, std::find) e smart pointers (std::shared_ptr) — todos são implementados usando templates.
Neste artigo, vamos analisar o que são templates em C++, como declará-los e usá-los corretamente, além de examinar exemplos práticos.
Parte principal: Como usar Generics (templates) em C++
1. Function Templates
A maneira mais simples de começar a trabalhar com generics é criar um function template. Em vez de escrever várias versões sobrecarregadas, você escreve uma, parametrizada pelo tipo.
Sintaxe:
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}A palavra-chave template informa ao compilador que isso é um template. <typename T> declara o parâmetro de tipo T. Você pode usar class em vez de typename — não há diferença.
Exemplo de uso:
#include <iostream>#include <string>
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}
int main() { std::cout << max(10, 20) << std::endl; // int: 20 std::cout << max(3.14, 2.71) << std::endl; // double: 3.14 std::cout << max(std::string("C++"), std::string("Python")) << std::endl; // string: Python return 0;}Importante: O compilador gera uma versão separada da função para cada tipo usado. Isso é chamado de instanciação de template. Com isso, alcança-se a máxima performance — não há overhead de chamadas virtuais.
Vários parâmetros de tipo:
template <typename T1, typename T2>void printPair(T1 a, T2 b) { std::cout << "(" << a << ", " << b << ")" << std::endl;}// printPair(1, "hello"); // Saída: (1, hello)2. Class Templates
Class templates permitem criar estruturas de dados genéricas. O exemplo clássico é std::vector<T>. Você pode criar sua própria classe template, por exemplo, uma pilha simples:
template <typename T>class Stack {private: T* data; size_t size; size_t capacity;public: Stack(size_t cap = 10) : size(0), capacity(cap) { data = new T[capacity]; } ~Stack() { delete[] data; }
void push(const T& value) { if (size < capacity) { data[size++] = value; } }
T pop() { if (size > 0) { return data[--size]; } throw std::out_of_range("Stack is empty"); }
bool isEmpty() const { return
```