Menu
Coddy logo textTech

Plantillas variádicas

Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 67 de 104.

¿Qué pasa si necesitas una función que acepte cualquier número de argumentos de cualquier tipo? Las plantillas tradicionales requieren que especifiques exactamente cuántos parámetros de tipo necesitas. Las plantillas variádicas resuelven esto permitiendo que las plantillas acepten un número arbitrario de argumentos de plantilla.

La sintaxis utiliza ... (puntos suspensivos) para crear un paquete de parámetros:

template <typename... Args>
void printAll(Args... args) {
    // Args es un paquete de parámetros de plantilla
    // args es un paquete de parámetros de función
}

Para procesar los argumentos, normalmente se utiliza la recursividad con un caso base. El paquete de parámetros se expande un argumento a la vez hasta que no queda ninguno:

// Caso base: no quedan argumentos
void print() {
    std::cout << std::endl;
}

// Caso recursivo: procesar el primer argumento, recurrir con el resto
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Expandir los argumentos restantes
}

print(1, 3.14, "hello", 'x');  // Salida: 1 3.14 hello x

También puedes usar expresiones de plegado (C++17) para operaciones más simples sin recursividad explícita:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Expresión de plegado: suma todos los argumentos
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Salida: 10

Las plantillas variádicas impulsan muchas características de la biblioteca estándar como std::make_unique, std::tuple y std::function. Permiten funciones con seguridad de tipos que funcionan con cualquier combinación de tipos y cantidades de argumentos.

challenge icon

Desafío

Fácil

Vamos a construir un sistema de registro flexible que utiliza plantillas variádicas para manejar mensajes con cualquier número de argumentos. Crearás utilidades que pueden concatenar valores, contar argumentos y mostrar una salida formateada, todo utilizando paquetes de parámetros (parameter packs).

Organizarás tu código en dos archivos:

  • Variadic.h: Define aquí tus funciones de plantilla variádica.

    Crea una función printAll que imprima todos sus argumentos separados por espacios, seguidos de una nueva línea. Utiliza el enfoque recursivo con un caso base que imprima solo una nueva línea, y un caso recursivo que imprima el primer argumento, un espacio, y luego realice la recursión con los argumentos restantes.

    Crea una función countArgs que devuelva el número de argumentos que se le pasan. Utiliza sizeof... para obtener el tamaño del paquete de parámetros.

    Crea una función sum que sume todos los argumentos numéricos y devuelva el resultado. Utiliza una expresión de plegado (fold expression) con el operador +.

    Crea una función product que multiplique todos los argumentos numéricos y devuelva el resultado. Utiliza una expresión de plegado (fold expression) con el operador *.

  • main.cpp: Lee cuatro entradas (cada una en una línea separada):
    1. Un entero
    2. Un double
    3. Un string
    4. Un carácter

    Demuestra tus plantillas variádicas mediante:

    1. Llamar a printAll con las cuatro entradas e imprimir el resultado
    2. Imprimir el recuento de argumentos al llamar a countArgs con el entero, el double y el carácter: Argument count: <count>
    3. Calcular la suma del entero, el double y los valores 10 y 5, luego imprimir: Sum: <result>
    4. Calcular el producto del entero y los valores 2 y 3, luego imprimir: Product: <result>
    5. Llamar a printAll solo con el string (un solo argumento)
    6. Llamar a printAll sin argumentos (debería imprimir solo una nueva línea)

Por ejemplo, con las entradas 5, 2.5, Hello, y X:

5 2.5 Hello X 
Argument count: 3
Sum: 22.5
Product: 30
Hello 

Observa cómo printAll maneja cualquier número de argumentos de tipos mixtos, desde cuatro argumentos hasta cero. Las funciones sum y product utilizan expresiones de plegado para combinar elegantemente todos los valores sin recursión explícita. Tu función countArgs demuestra cómo sizeof... te proporciona el tamaño del paquete en tiempo de compilación.

Hoja de referencia

Las plantillas variádicas permiten que las plantillas acepten un número arbitrario de argumentos de plantilla utilizando la sintaxis ... (puntos operativos) para crear un paquete de parámetros (parameter pack):

template <typename... Args>
void printAll(Args... args) {
    // Args es un paquete de parámetros de plantilla
    // args es un paquete de parámetros de función
}

Procesa los argumentos utilizando recursividad con un caso base:

// Caso base: no quedan argumentos
void print() {
    std::cout << std::endl;
}

// Caso recursivo: procesa el primer argumento, recurre con el resto
template <typename T, typename... Rest>
void print(T first, Rest... rest) {
    std::cout << first << " ";
    print(rest...);  // Expande los argumentos restantes
}

print(1, 3.14, "hello", 'x');  // Salida: 1 3.14 hello x

Utiliza sizeof... para obtener el número de argumentos en un paquete de parámetros:

template <typename... Args>
size_t countArgs(Args... args) {
    return sizeof...(args);
}

Utiliza expresiones de plegado (fold expressions) (C++17) para operaciones más sencillas sin recursividad explícita:

template <typename... Args>
auto sum(Args... args) {
    return (args + ...);  // Expresión de plegado: suma todos los argumentos
}

std::cout << sum(1, 2, 3, 4) << std::endl;  // Salida: 10

Las expresiones de plegado funcionan con otros operadores como la multiplicación:

template <typename... Args>
auto product(Args... args) {
    return (args * ...);  // Multiplica todos los argumentos
}

Pruébalo tú mismo

#include <iostream>
#include <string>
#include "Variadic.h"

using namespace std;

int main() {
    // Leer entradas
    int intVal;
    double doubleVal;
    string strVal;
    char charVal;
    
    cin >> intVal;
    cin >> doubleVal;
    cin >> strVal;
    cin >> charVal;
    
    // TODO: Llamar a printAll con las cuatro entradas
    
    // TODO: Imprimir el conteo de argumentos usando countArgs con intVal, doubleVal y charVal
    // Formato: "Argument count: <count>"
    
    // TODO: Calcular e imprimir la suma de intVal, doubleVal, 10 y 5
    // Formato: "Sum: <result>"
    
    // TODO: Calcular e imprimir el producto de intVal, 2 y 3
    // Formato: "Product: <result>"
    
    // TODO: Llamar a printAll solo con el string
    
    // TODO: Llamar a printAll sin argumentos
    
    return 0;
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos