Menu
Coddy logo textTech

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: hello

Las 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 especializada

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á en lugar de la plantilla genérica.

challenge icon

Desafío

Fácil

Construyamos 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 llamada Formatter que 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 Formatter debe tener:

    • Un método format() que reciba un valor de tipo T e imprima: Value: <value>

    Crea una especialización completa para bool que imprima texto legible en lugar de 1 o 0:

    • El método format() debe imprimir: Boolean: true o Boolean: 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 formatPair que reciba dos valores del mismo tipo y los imprima juntos como: Pair: [<first>, <second>]

    Crea una especialización de formatPair para const char* que encierre cada cadena entre comillas: Pair: ["<first>", "<second>"]

  • main.cpp: Lee cuatro entradas (cada una en una línea independiente):
    1. Un entero
    2. Un double
    3. Un booleano como cadena (true o false)
    4. Un valor de cadena

    Demuestra tus formateadores creando los objetos Formatter adecuados y llamando a format() para cada tipo:

    1. Formatea el entero usando Formatter<int>
    2. Formatea el double usando Formatter<double>
    3. Formatea el booleano usando Formatter<bool> (convierte primero la entrada de cadena a un bool)
    4. Formatea la cadena usando Formatter<const char*>

    Después, demuestra la especialización de la plantilla de función:

    1. Llama a formatPair con dos enteros: 10 y 20
    2. Llama a formatPair con 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;
}
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

Practica por tu cuenta: Compilador de C++ online