Templates de Função
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 64 de 104.
Imagine escrever uma função para encontrar o máximo de dois inteiros, e depois perceber que precisa da mesma lógica para doubles, e novamente para strings. Sem templates, você escreveria funções quase idênticas para cada tipo. Templates de função resolvem isso permitindo que você escreva a lógica uma vez e faça com que o compilador gere versões específicas para cada tipo automaticamente.
Um template de função usa a palavra-chave template seguida por parâmetros de template entre colchetes angulares:
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << maximum(5, 3) << std::endl; // Usa a versão int
std::cout << maximum(3.14, 2.71) << std::endl; // Usa a versão double
std::cout << maximum('a', 'z') << std::endl; // Usa a versão char
}O compilador examina cada chamada e gera uma função concreta para esse tipo específico. Esse processo é chamado de instanciação de template. Você também pode especificar explicitamente o tipo quando necessário:
std::cout << maximum<double>(5, 3.14) << std::endl; // Força a versão doubleTemplates podem ter múltiplos parâmetros de tipo, permitindo designs ainda mais flexíveis:
template <typename T, typename U>
void printPair(T first, U second) {
std::cout << first << ", " << second << std::endl;
}
printPair(42, "hello"); // T=int, U=const char*
printPair(3.14, 100); // T=double, U=intTemplates de função fornecem polimorfismo em tempo de compilação - o tipo é determinado quando o código é compilado, não em tempo de execução. Isso significa zero overhead em tempo de execução em comparação com funções virtuais, tornando os templates ideais para código genérico onde o desempenho é crítico.
Desafio
FácilVamos construir um kit de ferramentas utilitárias usando templates de função para criar operações reutilizáveis que funcionam com qualquer tipo compatível. Você organizará suas funções genéricas em um arquivo de cabeçalho e demonstrará a flexibilidade delas em seu programa principal.
Você criará dois arquivos:
MathUtils.h: Defina uma coleção de templates de função que realizam operações comuns:minimum— uma função template que recebe dois valores do mesmo tipo e retorna o menor deles.clamp— uma função template que recebe três parâmetros: um valor, um limite inferior (low bound) e um limite superior (high bound). Ela retorna o valor restringido dentro dos limites (retorna low se o valor for menor que low, high se o valor for maior que high, caso contrário, retorna o próprio valor).swapValues— uma função template que recebe duas referências do mesmo tipo e troca seus valores.main.cpp: Leia seis entradas (cada uma em uma linha separada):- Primeiro número inteiro
- Segundo número inteiro
- Um valor double para restringir (clamp)
- Limite inferior (double)
- Limite superior (double)
- Um caractere
Demonstre seus templates:
- Encontrando o mínimo dos dois inteiros e imprimindo:
Min of <a> and <b>: <result> - Encontrando o mínimo entre o caractere
'm'e o seu caractere de entrada, imprimindo:Min of m and <char>: <result> - Restringindo (clamping) o valor double e imprimindo:
Clamp <value> to [<low>, <high>]: <result> - Restringindo o primeiro inteiro para o intervalo [0, 100] e imprimindo:
Clamp <value> to [0, 100]: <result> - Trocando os dois inteiros e imprimindo:
After swapValues: <a>, <b>
Por exemplo, com as entradas 25, 10, 3.7, 1.0, 5.0 e z:
Min of 25 and 10: 10
Min of m and z: m
Clamp 3.7 to [1, 5]: 3.7
Clamp 25 to [0, 100]: 25
After swapValues: 10, 25Observe como cada função template funciona perfeitamente com inteiros, doubles e caracteres — o compilador gera a versão apropriada para cada tipo que você usa. Sua função swapValues deve modificar as variáveis originais através de referências, demonstrando que templates funcionam com parâmetros de referência assim como funções regulares.
Folha de consulta
Templates de função permitem que você escreva funções genéricas que funcionam com múltiplos tipos. Use a palavra-chave template seguida pelos parâmetros do template:
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}O compilador gera automaticamente versões específicas para cada tipo através da instanciação de template:
maximum(5, 3); // Gera a versão int
maximum(3.14, 2.71); // Gera a versão double
maximum('a', 'z'); // Gera a versão charVocê pode especificar explicitamente o tipo quando necessário:
maximum<double>(5, 3.14); // Força a versão doubleTemplates suportam múltiplos parâmetros de tipo:
template <typename T, typename U>
void printPair(T first, U second) {
std::cout << first << ", " << second << std::endl;
}
printPair(42, "hello"); // T=int, U=const char*Templates funcionam com parâmetros de referência para modificar valores originais:
template <typename T>
void swapValues(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}Templates de função fornecem polimorfismo em tempo de compilação com zero de sobrecarga em tempo de execução, tornando-os ideais para código genérico onde a performance é crítica.
Experimente você mesmo
#include <iostream>
#include "MathUtils.h"
using namespace std;
int main() {
// Ler entradas
int a, b;
double value, low, high;
char ch;
cin >> a;
cin >> b;
cin >> value;
cin >> low;
cin >> high;
cin >> ch;
// TODO: Use o template minimum para encontrar o mínimo de dois inteiros
// Print: "Min of <a> and <b>: <result>"
// TODO: Use o template minimum para encontrar o mínimo de 'm' e o caractere de entrada
// Print: "Min of m and <char>: <result>"
// TODO: Use o template clamp no valor double
// Print: "Clamp <value> to [<low>, <high>]: <result>"
// TODO: Use o template clamp no primeiro inteiro com o intervalo [0, 100]
// Print: "Clamp <value> to [0, 100]: <result>"
// TODO: Use o template swapValues nos dois inteiros
// Print: "After swapValues: <a>, <b>"
return 0;
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de OOP
Arquivos ExternosBuild e Compilação em C++Arquivos Header e SourceNamespaces e EscopoIntrodução a OOP em C++Classes vs ObjetosO Ponteiro 'this'Métodos (Funções Membro)Atributos (Membros de Dados)Básico de Ctors e DtorsRecapitulação - Calculadora Simples4Propriedades de Classe
Membros de Instância vs EstáticosGetters e SettersFunções de Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRecapitulação - Gerenciador de Conta Bancária7Herança
Herança BásicaNíveis de Acesso na HerançaOrdem de Chamada de Ctor e DtorSobrescrita de MétodosFunções Virtuais e VTableHerança MúltiplaHerança VirtualRecapitulação - Hierarquia de Funcionários2Gerenciamento de Memória
Memória Stack vs HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Smart Pointers em C++RAII em C++Recapitulação - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de Acesso em C++Especificadores de Acesso em DetalhesOcultação de InformaçãoStruct vs ClassClasses Aninhadas e InternasRecapitulação - Sistema de Registros de Alunos8Polimorfismo
Polimorfismo de Compilação vs Tempo de ExecuçãoSobrecarga de FunçõesFunções Virtuais RevisitadasFunções Virtuais PurasClasses AbstratasDesign de Interfaces em C++Dynamic Casting e RTTIRecapitulação - Calculadora de Formas3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentaçãoListas de Inicialização do ConstrutorConstrutores DelegadosMergulho Profundo em DestrutoresRegra dos Três / Cinco / ZeroRecapitulação - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de StreamSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçãoTemplates de ClasseEspecialização de TemplatesTemplates VariádicosFundamentos de SFINAE e Type TraitsRecapitulação - Container Genérico