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

Las 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 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á por encima de la plantilla genérica.

challenge icon

Desafío

Fácil

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

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

    Crea una especialización completa para bool que imprima texto legible por humanos 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>"

    También crea una plantilla de función llamada formatPair que tome dos valores del mismo tipo y los imprima juntos como: Pair: [<first>, <second>]

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

  • main.cpp: Lee cuatro entradas (cada una en una línea separada):
    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 apropiados 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*>

    Luego 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 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;
}
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