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é ocurre 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 al permitir 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 usas recursión con un caso base. El paquete de parámetros se expande argumento por argumento hasta que no queda ninguno:
// 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 xTambién puedes usar expresiones fold (C++17) para realizar operaciones más sencillas sin recursión explícita:
template <typename... Args>
auto sum(Args... args) {
return (args + ...); // Expresión fold: suma todos los argumentos
}
std::cout << sum(1, 2, 3, 4) << std::endl; // Salida: 10Las plantillas variádicas impulsan muchas funciones de la biblioteca estándar, como std::make_unique, std::tuple y std::function. Permiten crear funciones con seguridad de tipos que funcionan con cualquier combinación de tipos y cantidades de argumentos.
Desafío
FácilConstruyamos un sistema de registro flexible que use plantillas variádicas para gestionar mensajes con cualquier número de argumentos. Crearás utilidades que puedan concatenar valores, contar argumentos e imprimir resultados con formato: todo usando paquetes de parámetros.
Organizarás tu código en dos archivos:
Variadic.h: Define aquí tus funciones de plantilla variádicas.Crea una función
printAllque imprima todos sus argumentos separados por espacios, seguidos de un salto de línea. Usa el enfoque recursivo con un caso base que imprima solo un salto de línea y un caso recursivo que imprima el primer argumento, un espacio y después se llame recursivamente con los argumentos restantes.Crea una función
countArgsque devuelva el número de argumentos que se le pasaron. Usasizeof...para obtener el tamaño del paquete de parámetros.Crea una función
sumque sume todos los argumentos numéricos y devuelva el resultado. Usa una expresión de plegado con el operador+.Crea una función
productque multiplique todos los argumentos numéricos y devuelva el resultado. Usa una expresión de plegado con el operador*.main.cpp: Lee cuatro entradas (cada una en una línea separada):- Un entero
- Un double
- Una cadena
- Un carácter
Demuestra tus plantillas variádicas haciendo lo siguiente:
- Llamar a
printAllcon las cuatro entradas e imprimir el resultado - Imprimir el conteo de argumentos al llamar a
countArgscon el entero, el double y el carácter:Argument count: <count> - Calcular la suma del entero, el double y los valores 10 y 5; después, imprimir:
Sum: <result> - Calcular el producto del entero y los valores 2 y 3; después, imprimir:
Product: <result> - Llamar a
printAllsolo con la cadena (un único argumento) - Llamar a
printAllsin argumentos (debe imprimir solo un salto de 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 gestiona cualquier número de argumentos de tipos mixtos, desde cuatro argumentos hasta cero. Las funciones sum y product usan expresiones de plegado para combinar elegantemente todos los valores sin recursión explícita. Tu función countArgs demuestra cómo sizeof... proporciona el tamaño del paquete en tiempo de compilación.
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: Llama a printAll con las cuatro entradas
// TODO: Print argument count using countArgs with intVal, doubleVal, and charVal
// Format: "Argument count: <count>"
// TODO: Calcula e imprime la suma de intVal, doubleVal, 10 y 5
// Formato: "Sum: <result>"
// TODO: Calcula e imprime el producto de intVal, 2 y 3
// Formato: "Product: <result>"
// TODO: Llama a printAll solo con el string
// TODO: Llama a printAll sin argumentos
return 0;
}
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
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genéricoPractica por tu cuenta: Compilador de C++ online