C++: Introdução à Metaprogramação de Templates
Última atualização: 2026-08-26
Na lição 46 aprendemos sobre smart pointers avançados.
Agora, vamos tocar na "magia negra" do C++ — metaprogramação de templates.
Metaprogramação de templates é uma técnica que executa código em tempo de compilação, o que pode melhorar significativamente o desempenho em tempo de execução.
1. Visão Geral da Metaprogramação de Templates
(1) 1.1 O que é Metaprogramação de Templates?
Metaprogramação de Templates (TMP) é uma técnica que usa templates para realizar computações em tempo de compilação.
Características:
- Computações são feitas em tempo de compilação, com custo zero em tempo de execução
- Código é complexo, e tempos de compilação são longos
- Mensagens de erro são difíceis de ler
(2) 1.2 Por que Usar Metaprogramação de Templates?
| Vantagem | Descrição |
|---|---|
| Desempenho | Computação em tempo de compilação, mais rápido em tempo de execução |
| Segurança de tipo | Verificação de tipo em tempo de compilação |
| Genericidade | Escrever código verdadeiramente genérico |
2. Computação em Tempo de Compilação
(1) 2.1 Fatorial em Tempo de Compilação
Exemplo: Calculando fatorial com templates (Dificuldade ⭐⭐⭐)
▶ Exemplo 1: Exemplo de Código (Dificuldade ⭐)
#include <iostream>
// Primary template
template<int N>
struct Factorial {
static const int value = N * FactorialN-1::value;
};
// Specialization: termination condition
template<>
struct Factorial<0> {
static const int value = 1;
};
int main() {
std::cout << "5! = " << Factorial<5>::value << std::endl; // 120
// Result computed at compile time, used directly at runtime
return 0;
}
Saída:
5! =
▶ Exemplo 2: Exemplo de Código (Dificuldade ⭐)
💡 Dica:
- Use especialização de template como a condição de terminação da recursão
static const int valueé uma constante em tempo de compilação
(2) 2.2 Funções constexpr (C++11, Recomendado)
O C++11 introduziu constexpr, tornando a computação em tempo de compilação mais simples.
Exemplo: Calculando fatorial com constexpr (Dificuldade ⭐⭐)
#include <iostream>
constexpr int factorial(int n) {
return n <= 1 ? 1 : n * factorial(n - 1);
}
int main() {
constexpr int result = factorial(5); // Compile-time computation
std::cout << "5! = " << result << std::endl; // 120
return 0;
}
Saída:
5! =
💡 Dica:
constexpré mais conciso que metaprogramação de templates — prefiraconstexpr
3. Type Traits
(1) 3.1 O que são Type Traits?
Type Traits são técnicas para consultar ou modificar informações de tipo em tempo de compilação.
Exemplo: Usando type_traits para verificar tipos (Dificuldade ⭐⭐)
#include <iostream>
#include <type_traits>
int main() {
std::cout << std::is_integralint::value << std::endl; // 1 (true)
std::cout << std::is_integraldouble::value << std::endl; // 0 (false)
std::cout << std::is_pointer<int*>::value << std::endl; // 1 (true)
return 0;
}
(2) 3.2 Type Traits Personalizados
Exemplo: Detectando se uma classe tem uma função membro (Dificuldade ⭐⭐⭐⭐)
#include <iostream>
#include <type_traits>
// Detect whether there is a serialize member function
template<typename T>
struct has_serialize {
private:
template<typename U>
static auto test(int) -> decltype(std::declvalU().serialize(), std::true_type{});
template<typename U>
static std::false_type test(...);
public:
static const bool value = decltype(testT(0))::value;
};
struct Person {
void serialize() {}
};
int main() {
std::cout << has_serializePerson::value << std::endl; // 1 (true)
std::cout << has_serializeint::value << std::endl; // 0 (false)
return 0;
}
4. SFINAE
(1) 4.1 O que é SFINAE?
SFINAE (Substitution Failure Is Not An Error): Quando a substituição de argumentos de template falha, não é um erro — o compilador tentará outras sobrecargas.
Finalidade: Selecionar diferentes sobrecargas de função com base nas características do tipo.
(2) 4.2 Exemplo: SFINAE para Selecionar Sobrecargas (Dificuldade ⭐⭐⭐⭐)
#include <iostream>
#include <type_traits>
#include <string>
// Version 1: for integer types
template<typename T>
typename std::enable_if<std::is_integralT::value, std::string>::type
toString(T value) {
return std::to_string(value) + " (integer)";
}
// Version 2: for other types
template<typename T>
typename std::enable_if<!std::is_integralT::value, std::string>::type
toString(T value) {
return "Not an integer";
}
int main() {
std::cout << toString(42) << std::endl; // 42 (integer)
std::cout << toString(3.14) << std::endl; // Not an integer
return 0;
}
Saída:
(program output)
5. Templates Variádicos
(1) 5.1 O que são Templates Variádicos?
Templates Variádicos permitem que templates aceitem um número arbitrário de argumentos.
Exemplo: Imprimindo qualquer número de argumentos (Dificuldade ⭐⭐⭐)
#include <iostream>
// Recursion termination function
void print() {
std::cout << std::endl;
}
// Recursive print
template<typename T, typename... Args>
void print(T first, Args... rest) {
std::cout << first << " ";
print(rest...); // Recursive call
}
int main() {
print(1, 2.5, "hello", 'a');
// Output: 1 2.5 hello a
return 0;
}
6. Expressões Fold do C++17
(1) 6.1 Uso Básico
O C++17 introduziu expressões fold, que simplificam templates variádicos.
Exemplo: Somatório (Dificuldade ⭐⭐)
#include <iostream>
template<typename... Args>
auto sum(Args... args) {
return (args + ...); // Fold expression
}
int main() {
std::cout << sum(1, 2, 3, 4, 5) << std::endl; // 15
return 0;
}
▶ Exemplo 3: Computação em Tempo de Compilação com constexpr (Dificuldade ⭐)
#include <iostream>
// constexpr functions are evaluated at compile time
constexpr int square(int n) {
return n * n;
}
constexpr int cube(int n) {
return n * n * n;
}
int main() {
// Compile-time computation, results embedded directly
constexpr int sq = square(5);
constexpr int cb = cube(3);
std::cout << "5^2 = " << sq << std::endl;
std::cout << "3^3 = " << cb << std::endl;
// Static assertions verify compile-time evaluation
static_assert(sq == 25, "square(5) should be 25");
static_assert(cb == 27, "cube(3) should be 27");
return 0;
}
Saída:
5^2 =
3^3 =
❓ Perguntas Frequentes
P: Metaprogramação de templates é difícil de aprender? R: Sim. É recomendado dominar templates básicos antes de aprender metaprogramação. A maioria dos projetos não precisa de metaprogramação.
P: Quando devo usar metaprogramação de templates? R: - Escrevendo bibliotecas (como a STL) - Quando precisa de desempenho extremo - Quando precisa de verificação de tipo em tempo de compilação
P: constexpr pode substituir metaprogramação de templates? R: Parcialmente. O C++14/17 expandiu
constexpr, e muitas computações em tempo de compilação agora podem ser implementadas com funçõesconstexpr.
📖 Resumo
| Tópico | Pontos-chave |
|---|---|
| Computação em tempo de compilação | Recursão de template ou constexpr |
| Type traits | Biblioteca type_traits |
| SFINAE | Selecionar sobrecargas com base no tipo |
| Templates variádicos | Aceitar número arbitrário de argumentos |
| Expressões fold | C++17, simplifica templates variádicos |
📝 Exercícios
-
Básico (Dificuldade ⭐): Escreva uma função constexpr para calcular fatorial, avaliada em tempo de compilação. Verifique o resultado usando static_assert.
-
Intermediário (Dificuldade ⭐⭐): Use std::enable_if para implementar um template de função que só é habilitado quando T é um tipo inteiro. Tipos de ponto flutuante devem causar erro de compilação.
-
Desafio (Dificuldade ⭐⭐⭐): Use templates variádicos para implementar uma função print_all que aceita qualquer número e tipo de argumentos e os imprime um a um. Dica: Use expansão recursiva ou expressões fold (C++17).
- Metaprogramação de templates: computação em tempo de compilação, custo zero em tempo de execução
- Avaliação em tempo de compilação com constexpr
- SFINAE: Substitution Failure Is Not An Error
- Type traits consultam propriedades de tipo
- Templates variádicos lidam com números arbitrários de argumentos
Próxima lição: Novos Recursos do C++17/20 (#48)