Templates Variádicos
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy — lição 67 de 104.
E se você precisar de uma função que aceite qualquer número de argumentos de quaisquer tipos? Templates tradicionais exigem que você especifique exatamente quantos parâmetros de tipo você precisa. Variadic templates resolvem isso permitindo que os templates aceitem um número arbitrário de argumentos de template.
A sintaxe usa ... (reticências) para criar um pacote de parâmetros (parameter pack):
template <typename... Args>
void printAll(Args... args) {
// Args é um pacote de parâmetros de template
// args é um pacote de parâmetros de função
}Para processar os argumentos, você normalmente usa recursão com um caso base. O pacote de parâmetros (parameter pack) expande um argumento por vez até que nenhum reste:
// Caso base: nenhum argumento restante
void print() {
std::cout << std::endl;
}
// Caso recursivo: processa o primeiro argumento, recorre com o restante
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
std::cout << first << " ";
print(rest...); // Expande os argumentos restantes
}
print(1, 3.14, "hello", 'x'); // Saída: 1 3.14 hello xVocê também pode usar fold expressions (C++17) para operações mais simples sem recursão explícita:
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Fold expression: soma todos os argumentos
}
std::cout << sum(1, 2, 3, 4) << std::endl; // Saída: 10Templates variádicos sustentam muitos recursos da biblioteca padrão, como std::make_unique, std::tuple e std::function. Eles permitem funções type-safe que funcionam com qualquer combinação de tipos e quantidades de argumentos.
Desafio
FácilVamos construir um sistema de log flexível que utiliza templates variádicos para lidar com mensagens com qualquer número de argumentos. Você criará utilitários que podem concatenar valores, contar argumentos e imprimir saídas formatadas — tudo usando pacotes de parâmetros (parameter packs).
Você organizará seu código em dois arquivos:
Variadic.h: Defina suas funções de template variádico aqui.Crie uma função
printAllque imprime todos os seus argumentos separados por espaços, seguidos por uma nova linha. Use a abordagem recursiva com um caso base que imprime apenas uma nova linha, e um caso recursivo que imprime o primeiro argumento, um espaço, e então faz a recursão com os argumentos restantes.Crie uma função
countArgsque retorna o número de argumentos passados para ela. Usesizeof...para obter o tamanho do pacote de parâmetros.Crie uma função
sumque soma todos os argumentos numéricos e retorna o resultado. Use uma expressão de dobra (fold expression) com o operador+.Crie uma função
productque multiplica todos os argumentos numéricos e retorna o resultado. Use uma expressão de dobra (fold expression) com o operador*.main.cpp: Leia quatro entradas (cada uma em uma linha separada):- Um inteiro
- Um double
- Uma string
- Um caractere
Demonstre seus templates variádicos:
- Chamando
printAllcom todas as quatro entradas e imprimindo o resultado - Imprimindo a contagem de argumentos ao chamar
countArgscom o inteiro, o double e o caractere:Argument count: <count> - Calculando a soma do inteiro, do double e dos valores 10 e 5, e então imprimindo:
Sum: <result> - Calculando o produto do inteiro e dos valores 2 e 3, e então imprimindo:
Product: <result> - Chamando
printAllapenas com a string (argumento único) - Chamando
printAllsem argumentos (deve imprimir apenas uma nova linha)
Por exemplo, com as entradas 5, 2.5, Hello e X:
5 2.5 Hello X
Argument count: 3
Sum: 22.5
Product: 30
Hello
Observe como printAll lida com qualquer número de argumentos de tipos mistos, de quatro argumentos até zero. As funções sum e product usam expressões de dobra para combinar elegantemente todos os valores sem recursão explícita. Sua função countArgs demonstra como sizeof... fornece o tamanho do pacote em tempo de compilação.
Folha de consulta
Templates variádicos permitem que templates aceitem um número arbitrário de argumentos de template usando a sintaxe ... (reticências) para criar um pacote de parâmetros (parameter pack):
template <typename... Args>
void printAll(Args... args) {
// Args é um pacote de parâmetros de template
// args é um pacote de parâmetros de função
}Processe argumentos usando recursão com um caso base:
// Caso base: nenhum argumento restante
void print() {
std::cout << std::endl;
}
// Caso recursivo: processa o primeiro argumento, recorre com o restante
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
std::cout << first << " ";
print(rest...); // Expande os argumentos restantes
}
print(1, 3.14, "hello", 'x'); // Saída: 1 3.14 hello xUse sizeof... para obter o número de argumentos em um pacote de parâmetros:
template <typename... Args>
size_t countArgs(Args... args) {
return sizeof...(args);
}Use expressões de dobra (fold expressions) (C++17) para operações mais simples sem recursão explícita:
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Expressão de dobra: soma todos os argumentos
}
std::cout << sum(1, 2, 3, 4) << std::endl; // Saída: 10Expressões de dobra funcionam com outros operadores como multiplicação:
template <typename... Args>
auto product(Args... args) {
return (args * ...); // Multiplica todos os argumentos
}Experimente você mesmo
#include <iostream>
#include <string>
#include "Variadic.h"
using namespace std;
int main() {
// Ler as entradas
int intVal;
double doubleVal;
string strVal;
char charVal;
cin >> intVal;
cin >> doubleVal;
cin >> strVal;
cin >> charVal;
// TODO: Chamar printAll com todas as quatro entradas
// TODO: Imprimir a contagem de argumentos usando countArgs com intVal, doubleVal e charVal
// Formato: "Argument count: <count>"
// TODO: Calcular e imprimir a soma de intVal, doubleVal, 10 e 5
// Formato: "Sum: <result>"
// TODO: Calcular e imprimir o produto de intVal, 2 e 3
// Formato: "Product: <result>"
// TODO: Chamar printAll apenas com a string
// TODO: Chamar printAll sem argumentos
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