Introdução: o que são generics em C++ e para que servem
No mundo da programação moderna, a reutilização de código é um dos princípios-chave. Se você escreve uma função de soma para int, e depois para double e float, mais cedo ou mais tarde vai querer evitar a duplicação. É aqui que os generics (programação genérica) vêm ao resgate. Em C++, este conceito é implementado através de templates.
Generics permitem escrever código que funciona com qualquer tipo de dado, sem sacrificar o desempenho (ao contrário de funções virtuais ou void*). O compilador gera uma versão separada da função ou classe para cada tipo específico, proporcionando velocidade máxima de execução.
Neste artigo, vamos explorar para que servem os generics em C++, como declará-los e usá-los corretamente na prática, além de abordar erros comuns e técnicas avançadas.
Fundamentos da sintaxe: como declarar e usar um template
Um template em C++ é declarado com a palavra-chave template e colchetes angulares <>, dentro dos quais é especificado o parâmetro de tipo (geralmente typename ou class).
Função template
O exemplo mais simples é uma função que retorna o máximo de dois valores:
template <typename T>T max(T a, T b) { return (a > b) ? a : b;}
// Uso:int main() { std::cout << max(3, 7) << std::endl; // 7 std::cout << max(3.14, 2.71) << std::endl; // 3.14 std::cout << max('a', 'z') << std::endl; // z return 0;}Observe: o compilador deduz o tipo T automaticamente com base nos argumentos passados. Se precisar especificar o tipo explicitamente, você pode escrever max<double>(3.14, 2.71).
Classe template
Templates também funcionam com classes. Um exemplo clássico é um contêiner simples "par":
template <typename T1, typename T2>class Pair {private: T1 first; T2 second;public: Pair(T1 f, T2 s) : first(f), second(s) {} T1 getFirst() const { return first; } T2 getSecond() const { return second; }};
// Uso:Pair<int, std::string> student(1, "Alice");std::cout << student.getFirst() << " " << student.getSecond();Aqui, T1 e T2 são parâmetros do template, que podem ser de qualquer tipo.
Exemplos práticos: de contêineres simples a algoritmos
Vamos considerar alguns cenários reais onde os generics são indispensáveis.
Ordenação universal
Vamos escrever uma função template para ordenar um array de qualquer tipo que suporte o operador <:
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]) std::swap(arr[j], arr[j + 1]);}
// Exemplo:int intArr[] = {5, 2, 8, 1};bubbleSort(intArr, 4); // Funciona com int
double dblArr[] = {3.14, 2.71, 1.41};bubbleSort(dblArr, 3); // Funciona com doublePilha com tipo de dado arbitrário
Vamos implementar uma pilha simples baseada em template:
template <typename T>class Stack {private: std::vector<T> elements;public: void push(const T& value) { elements.push_back(value); } void pop() { if (!elements.empty()) elements.pop_back(); } T top() const { return elements.back(); } bool empty() const { return elements.empty(); }};
// Uso:Stack<int> intStack;intStack.push(10);intStack.push(20);std::cout << intStack.top(); // 20Algoritmo "encontrar e substituir"
Função template que substitui todas as ocorrências de um valor por outro em um contêiner:
templa