Especialización de plantillas
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy. Lección 66 de 104.
A veces, una plantilla generic no funciona bien para todos los tipos. Por ejemplo, comparar cadenas de estilo C con > compara las direcciones de los punteros, no el texto real. La especialización de plantillas te permite proporcionar una implementación personalizada para tipos específicos, mientras conservas la versión generic para todo lo demás.
Una especialización full reemplaza toda la plantilla para un tipo específico. La declaras con una lista de parámetros de plantilla vacía y especificas el tipo concreto:
// Plantilla primaria
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Especialización completa para const char*
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Printer<int> intPrinter;
intPrinter.print(42); // Salida: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // Salida: String: helloLas plantillas de funciones también pueden especializarse:
template <typename T>
bool isEqual(T a, T b) {
return a == b;
}
template <>
bool isEqual<const char*>(const char* a, const char* b) {
return std::strcmp(a, b) == 0;
}
isEqual(5, 5); // Usa la versión genérica
isEqual("hi", "hi"); // Usa la versión especializadaEl compilador siempre prefiere la coincidencia más específica: si existe una especialización para el tipo exacto que se está utilizando, se elegirá en lugar de la plantilla genérica.
Desafío
FácilConstruyamos un sistema de formateo consciente del tipo que demuestre cómo la especialización de plantillas permite personalizar el comportamiento para tipos específicos, manteniendo al mismo tiempo un recurso genérico para todo lo demás.
Crearás dos archivos para organizar tu código de plantilla:
Formatter.h: Define una plantilla de clase llamadaFormatterque formatee valores para su visualización. Tu plantilla genérica debe funcionar con cualquier tipo, pero también crearás versiones especializadas para los tipos que necesiten un tratamiento personalizado.La plantilla principal
Formatterdebe tener:- Un método
format()que reciba un valor de tipo T e imprima:Value: <value>
Crea una especialización completa para
boolque imprima texto legible en lugar de 1 o 0:- El método
format()debe imprimir:Boolean: trueoBoolean: false
Crea una especialización completa para
const char*que añada comillas alrededor de las cadenas:- El método
format()debe imprimir:String: "<value>"
Crea también una plantilla de función llamada
formatPairque reciba dos valores del mismo tipo y los imprima juntos como:Pair: [<first>, <second>]Crea una especialización de
formatPairparaconst char*que encierre cada cadena entre comillas:Pair: ["<first>", "<second>"]- Un método
main.cpp: Lee cuatro entradas (cada una en una línea independiente):- Un entero
- Un double
- Un booleano como cadena (
trueofalse) - Un valor de cadena
Demuestra tus formateadores creando los objetos
Formatteradecuados y llamando aformat()para cada tipo:- Formatea el entero usando
Formatter<int> - Formatea el double usando
Formatter<double> - Formatea el booleano usando
Formatter<bool>(convierte primero la entrada de cadena a un bool) - Formatea la cadena usando
Formatter<const char*>
Después, demuestra la especialización de la plantilla de función:
- Llama a
formatPaircon dos enteros: 10 y 20 - Llama a
formatPaircon dos cadenas C:"hello"y"world"
Por ejemplo, con las entradas 42, 3.14, true y Hello:
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]Observa cómo la plantilla genérica trata los enteros y los doubles de forma idéntica, mientras que las versiones especializadas para bool y const char* proporcionan un formateo personalizado. El compilador selecciona automáticamente la coincidencia más específica para cada tipo que utilices.
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// Lee las entradas
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// Convierte string a bool
bool boolVal = (boolStr == "true");
// TODO: Crea Formatter<int> y formatea el entero
// TODO: Crea Formatter<double> y formatea el double
// TODO: Crea Formatter<bool> y formatea el booleano
// TODO: Crea Formatter<const char*> y formatea el string
// Pista: Usa strVal.c_str() para obtener const char*
// TODO: Llama a formatPair con dos enteros: 10 y 20
// TODO: Llama a formatPair con dos C-strings: "hello" y "world"
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