Menu
Coddy logo textTech

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 x

Você 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: 10

Templates 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.

challenge icon

Desafio

Fácil

Vamos 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 printAll que 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 countArgs que retorna o número de argumentos passados para ela. Use sizeof... para obter o tamanho do pacote de parâmetros.

    Crie uma função sum que 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 product que 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):
    1. Um inteiro
    2. Um double
    3. Uma string
    4. Um caractere

    Demonstre seus templates variádicos:

    1. Chamando printAll com todas as quatro entradas e imprimindo o resultado
    2. Imprimindo a contagem de argumentos ao chamar countArgs com o inteiro, o double e o caractere: Argument count: <count>
    3. Calculando a soma do inteiro, do double e dos valores 10 e 5, e então imprimindo: Sum: <result>
    4. Calculando o produto do inteiro e dos valores 2 e 3, e então imprimindo: Product: <result>
    5. Chamando printAll apenas com a string (argumento único)
    6. Chamando printAll sem 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 x

Use 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: 10

Expressõ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;
}
quiz iconTeste seus conhecimentos

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