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? Os templates tradicionais exigem que você especifique exatamente quantos parâmetros de tipo são necessários. Templates variádicos 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:
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, normalmente você usa recursão com um caso-base. O pacote de parâmetros se 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, recursa 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 expressões de fold (C++17) para operações mais simples sem recursão explícita:
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Expressão fold: adiciona todos os argumentos
}
std::cout << sum(1, 2, 3, 4) << std::endl; // Saída: 10Os templates variádicos viabilizam muitos recursos da biblioteca padrão, como std::make_unique, std::tuple e std::function. Eles permitem funções com segurança de tipos que funcionam com qualquer combinação de tipos e quantidades de argumentos.
Desafio
FácilVamos criar um sistema de registro flexível que usa 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 uma saída formatada — tudo usando pacotes de parâmetros.
Você organizará seu código em dois arquivos:
Variadic.h: defina suas funções de template variádicas aqui.Crie uma função
printAllque imprima 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, em seguida, faz a recursão com os argumentos restantes.Crie uma função
countArgsque retorne o número de argumentos passados a ela. Usesizeof...para obter o tamanho do pacote de parâmetros.Crie uma função
sumque some todos os argumentos numéricos e retorne o resultado. Use uma expressão de dobra com o operador+.Crie uma função
productque multiplique todos os argumentos numéricos e retorne o resultado. Use uma expressão de dobra 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 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, em seguida, imprimindo:
Sum: <result> - Calculando o produto do inteiro e dos valores 2 e 3 e, em seguida, imprimindo:
Product: <result> - Chamando
printAllapenas com a string (um único argumento) - 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 todos os valores de forma elegante, sem recursão explícita. Sua função countArgs demonstra como sizeof... fornece o tamanho do pacote em tempo de compilação.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Variadic.h"
using namespace std;
int main() {
// Ler 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: Print argument count using countArgs with intVal, doubleVal, and charVal
// Format: "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 POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online