C++: Fundamentos de Templates
Última atualização: 2026-08-26
Nas lições anteriores, as funções e classes que escrevemos só podiam lidar com um tipo.
Mas e se você quiser escrever uma função
maxque funcione comint,doubleestring?Templates resolvem esse problema — permitindo que funções e classes suportem qualquer tipo.
1. O que são Templates?
(1) 1.1 Templates na Vida Real*
| Analogia da Vida Real | Equivalente na Programação |
|---|---|
| Uma forma de bolo (pode despejar diferentes massas) | Template |
| Mala direta do Word (um template, preencher diferentes destinatários) | Template |
A essência dos templates: Deixar o compilador gerar automaticamente a versão apropriada de uma função ou classe com base nos tipos usados.
(2) 1.2 Por que Precisamos de Templates?
Sem templates (código repetido):
#include <iostream>
// max for int
int max(int a, int b) {
return a > b ? a : b;
}
// max for double
double max(double a, double b) {
return a > b ? a : b;
}
// ... You'd have to write N versions!
Com templates (reutilização de código):
#include <iostream>
// Function template
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << max(3, 5) << std::endl; // Compiler auto-generates int version
std::cout << max(3.14, 2.72) << std::endl; // Compiler auto-generates double version
return 0;
}
2. Templates de Função*
(1) 2.1 Sintaxe Básica*
template <typename TypeName>
ReturnType functionName(parameterList) {
// function body
}
💡 Ponto-chave: typename também pode ser escrito como class (são equivalentes), mas typename é recomendado (mais intuitivo).
▶ Exemplo 1: Template de Função (Dificuldade ⭐)
#include <iostream>
// Function template
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << "max(3, 5) = " << max(3, 5) << std::endl;
std::cout << "max(3.14, 2.72) = " << max(3.14, 2.72) << std::endl;
// You can also explicitly specify the type
std::cout << "max<int>(3, 5) = " << max<int>(3, 5) << std::endl;
return 0;
}
Saída:
max(3, 5) = 5
max(3.14, 2.72) = 3.14
max<int>(3, 5) = 5
3. Templates de Classe*
(1) 3.1 Sintaxe Básica*
template <typename TypeName>
class ClassName {
// members
};
▶ Exemplo 2: Template de Classe Pair (Dificuldade ⭐⭐)
#include <iostream>
#include <string>
// Class template: store a pair of values
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(const T1& a, const T2& b) : first(a), second(b) {}
void print() {
std::cout << "(" << first << ", " << second << ")" << std::endl;
}
};
int main() {
Pair<int, double> p1(3, 3.14);
Pair<std::string, int> p2("Alice", 20);
p1.print(); // (3, 3.14)
p2.print(); // (Alice, 20)
return 0;
}
Saída:
(3, 3.14)
(Alice, 20)
💡 Ponto-chave: Templates de classe devem ter seus tipos explicitamente especificados quando usados (a menos que o construtor possa deduzi-los).
4. Declaração e Definição de Template*
(1) 4.1 O Problema: Onde Colocar os Templates?*
Se você colocar a declaração do template em .h e a definição em .cpp, obterá um erro de linker!
Razão: Um template não é uma função/classe real — é uma "forma" que gera código em tempo de compilação.
- Se a declaração está em
.he a definição em.cpp, o compilador não sabe quais versões de tipo gerar ao compilar.cpp. - Apenas no ponto de uso é que o compilador sabe quais versões gerar.
(2) 4.2 Solução: Colocar Declaração e Definição Juntas*
Abordagem recomendada: Coloque tanto a declaração quanto a definição do template no arquivo .h.
// pair.h (header file)
#ifndef PAIR_H
#define PAIR_H
template <typename T1, typename T2>
class Pair {
private:
T1 first;
T2 second;
public:
Pair(const T1& a, const T2& b);
void print();
};
// Definition goes here too!
template <typename T1, typename T2>
Pair<T1, T2>::Pair(const T1& a, const T2& b) : first(a), second(b) {}
template <typename T1, typename T2>
void Pair<T1, T2>::print() {
std::cout << "(" << first << ", " << second << ")" << std::endl;
}
#endif
💡 Dica: Essa é a maior diferença entre templates e funções/classes comuns — templates tipicamente têm declaração e definição juntas.
5. Prática: Template de Array Simples*
▶ Exemplo 3: Implementando o Template de Classe Array (Dificuldade ⭐⭐⭐)
#include <iostream>
#include <cassert>
template <typename T>
class Array {
private:
T* data;
int size;
public:
Array(int sz) : size(sz) {
data = new T[size];
}
~Array() {
delete data;
}
// Copy constructor (deep copy)
Array(const Array& other) : size(other.size) {
data = new T[size];
for (int i = 0; i < size; i++) {
data[i] = other.data[i];
}
}
// Overload operator
T& operator(int index) {
assert(index >= 0 && index < size);
return data[index];
}
int getSize() const {
return size;
}
};
int main() {
Array<int> arr(5);
for (int i = 0; i < arr.getSize(); i++) {
arr[i] = (i + 1) * 10;
}
for (int i = 0; i < arr.getSize(); i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
return 0;
}
Saída:
10 20 30 40 50
▶ Exemplo 3: Template de Função (Dificuldade ⭐)
#include <iostream>
template<typename T>
T getMax(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << "int max: " << getMax(3, 5) << std::endl;
std::cout << "double max: " << getMax(3.14, 2.71) << std::endl;
std::cout << "string max: " << getMax("apple", "banana") << std::endl;
return 0;
}
Saída:
int max: 5
double max: 3.14
string max: banana
T.
P: Qual a diferença entre templates e sobrecarga de funções? R:> - Sobrecarga de funções: múltiplas funções com o mesmo nome mas tipos de parâmetros diferentes (o compilador determina qual chamar em tempo de compilação) > - Templates de função: uma "forma" a partir da qual o compilador gera automaticamente a versão apropriada com base nos tipos usados > > Recomendação: Se a lógica é exatamente a mesma (apenas o tipo difere), use templates; se a lógica difere, use sobrecarga. P: Templates podem suportar todos os tipos? R: Não! Templates exigem que os tipos suportem certas operações. > > template
<typename T>> T add(T a, T b) { > return a + b; // Requires T to support operator+ > } > > // If T is a custom class without an overloaded operator+, it will cause a compilation error. > P: Quando devo usar templates? R: Quando a lógica da sua função/classe é independente de tipo. > > Cenários típicos: > - Classes contêiner (std::vector,std::array) > - Algoritmos (std::sort,std::find) > - Funções utilitárias (std::max,std::min,std::swap)
P: Qual é a coisa mais importante sobre fundamentos de templates? R: Entenda os conceitos centrais primeiro, depois reforce-os através de exemplos práticos.
❓ Perguntas Frequentes
P: Declarações e definições de templates devem estar em arquivos de cabeçalho? R: Sim, templates exigem definições completas em tempo de compilação. Eles tipicamente são colocados em arquivos .h, ou você pode usar a palavra-chave export (C++20). P: Qual a diferença entre templates de classe e templates de função? R: Templates de função podem deduzir automaticamente os parâmetros de tipo, enquanto templates de classe exigem especificação explícita dos parâmetros de tipo.
📖 Resumo
- Templates de função: programação genérica, instanciados em tempo de compilação
- Parâmetros de template declarados com typename/class
- Templates de classe: abstrações genéricas como contêineres e smart pointers
- Especialização de template: implementações personalizadas para tipos específicos
- Vantagens dos templates: reutilização de código, segurança de tipo, custo zero
📝 Exercícios
-
Básico (Dificuldade ⭐): Escreva um template de função
T min(T a, T b)que retorne o menor de dois valores. -
Intermediário (Dificuldade ⭐⭐): Escreva um template de classe
BoxTcom um membro do tipoT, e funções membrovoid set(T val)eT get(). -
Desafio (Dificuldade ⭐⭐⭐): Estenda o template de classe
Arrayacima adicionando: -
void push_back(const T& val)(expansão dinâmica) -
void pop_back() -
Implemente com templates para que possa armazenar qualquer tipo
6. 🚀 Próximos Passos*
Agora que você aprendeu os fundamentos de templates, a seguir vamos estudar Contêineres STL (lição 34) — usando os contêineres prontos fornecidos pela biblioteca padrão do C++, para que você não precise reinventar a roda!