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 genérica 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 le permite proporcionar una implementación personalizada para tipos específicos mientras mantiene la versión genérica para todo lo demás.
Una especialización completa reemplaza la plantilla completa para un tipo específico. Se declara con una lista de parámetros de plantilla vacía y se especifica 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 se pueden especializar:
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á por encima de la plantilla genérica.
Desafío
FácilVamos a construir un sistema de formateo consciente del tipo que demuestra cómo la especialización de plantillas permite personalizar el comportamiento para tipos específicos mientras se mantiene un respaldo genérico para todo lo demás.
Crearás dos archivos para organizar el código de tus plantillas:
Formatter.h: Define una plantilla de clase llamadaFormatterque formatea valores para su visualización. Tu plantilla genérica debería funcionar con cualquier tipo, pero también crearás versiones especializadas para tipos que necesiten un manejo personalizado.La plantilla primaria
Formatterdebe tener:- Un método
format()que toma un valor de tipo T e imprime:Value: <value>
Crea una especialización completa para
boolque imprima texto legible por humanos 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>"
También crea una plantilla de función llamada
formatPairque tome dos valores del mismo tipo y los imprima juntos como:Pair: [<first>, <second>]Crea una especialización de
formatPairparaconst char*que envuelva cada cadena en comillas:Pair: ["<first>", "<second>"]- Un método
main.cpp: Lee cuatro entradas (cada una en una línea separada):- Un entero
- Un double
- Un booleano como cadena (
"true"o"false") - Un valor de cadena
Demuestra tus formateadores creando los objetos
Formatterapropiados 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*>
Luego demuestra la especialización de la plantilla de función:
- Llama a
formatPaircon dos enteros: 10 y 20 - Llama a
formatPaircon dos C-strings:"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 maneja 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.
Hoja de referencia
La especialización de plantillas le permite proporcionar implementaciones personalizadas para tipos específicos mientras mantiene una versión genérica para todos los demás tipos.
Sintaxis de Especialización Completa
Declare una especialización con una lista de parámetros de plantilla vacía template <> y especifique el tipo concreto:
// Primary template
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Full specialization for const char*
template <>
class Printer<const char*> {
public:
void print(const char* value) {
std::cout << "String: " << value << std::endl;
}
};
Especialización de Plantillas de Función
Las plantillas de función también pueden ser especializadas:
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;
}
Selección del Compilador
El compilador siempre prefiere la coincidencia más específica: si existe una especialización para el tipo exacto que se está utilizando, se elegirá sobre la plantilla genérica.
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// Leer entradas
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// Convertir string a bool
bool boolVal = (boolStr == "true");
// TODO: Crear Formatter<int> y formatear el entero
// TODO: Crear Formatter<double> y formatear el double
// TODO: Crear Formatter<bool> y formatear el booleano
// TODO: Crear Formatter<const char*> y formatear el string
// Hint: Usar strVal.c_str() para obtener const char*
// TODO: Llamar a formatPair con dos enteros: 10 y 20
// TODO: Llamar 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érico