Specializzazione dei template
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 66 di 104.
A volte un modello generico non funziona bene per ogni tipo. Per esempio, confrontare stringhe in stile C con > confronta gli indirizzi dei puntatori, non il testo effettivo. La specializzazione del modello ti permette di fornire un’implementazione personalizzata per tipi specifici, mantenendo la versione generica per tutto il resto.
Una specializzazione completa sostituisce l'intero modello per un tipo specifico. La dichiari con un elenco di parametri del modello vuoto e specifichi il tipo concreto:
// Template primario
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Specializzazione completa per 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); // Output: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // Output: String: helloAnche i modelli di funzione possono essere specializzati:
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 versione generica
isEqual("hi", "hi"); // Usa la versione specializzataIl compilatore preferisce sempre la corrispondenza più specifica: se esiste una specializzazione per il tipo esatto utilizzato, verrà scelta al posto del modello generico.
Sfida
FacileCostruiamo un sistema di formattazione consapevole del tipo che mostri come la specializzazione dei template consenta di personalizzare il comportamento per tipi specifici, mantenendo al contempo un fallback generico per tutto il resto.
Creerai due file per organizzare il codice dei template:
Formatter.h: definisci un template di classe chiamatoFormatterche formatta i valori da visualizzare. Il template generico dovrebbe funzionare con qualsiasi tipo, ma creerai anche versioni specializzate per i tipi che richiedono una gestione personalizzata.Il template primario
Formatterdovrebbe avere:- Un metodo
format()che accetta un valore di tipo T e stampa:Value: <value>
Crea una specializzazione completa per
boolche stampi testo leggibile invece di 1 o 0:- Il metodo
format()dovrebbe stampare:Boolean: trueoppureBoolean: false
Crea una specializzazione completa per
const char*che aggiunga virgolette attorno alle stringhe:- Il metodo
format()dovrebbe stampare:String: "<value>"
Crea anche un template di funzione chiamato
formatPairche accetti due valori dello stesso tipo e li stampi insieme nel formato:Pair: [<first>, <second>]Crea una specializzazione di
formatPairperconst char*che racchiuda ogni stringa tra virgolette:Pair: ["<first>", "<second>"]- Un metodo
main.cpp: leggi quattro input (ciascuno su una riga separata):- Un intero
- Un double
- Un booleano come stringa (
trueofalse) - Un valore stringa
Mostra come funzionano i tuoi formattatori creando gli oggetti
Formatterappropriati e chiamandoformat()per ciascun tipo:- Formatta l’intero usando
Formatter<int> - Formatta il double usando
Formatter<double> - Formatta il booleano usando
Formatter<bool>(prima converti l’input stringa in un bool) - Formatta la stringa usando
Formatter<const char*>
Mostra poi la specializzazione del template di funzione:
- Chiama
formatPaircon due interi: 10 e 20 - Chiama
formatPaircon due stringhe C:"hello"e"world"
Per esempio, con gli input 42, 3.14, true e Hello:
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]Nota come il template generico gestisce interi e double nello stesso modo, mentre le versioni specializzate per bool e const char* forniscono una formattazione personalizzata. Il compilatore seleziona automaticamente la corrispondenza più specifica per ciascun tipo utilizzato.
Provalo tu
#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 la cadena a bool
bool boolVal = (boolStr == "true");
// TODO: Crea Formatter<int> y da formato al entero
// TODO: Crea Formatter<double> y da formato al valor double
// TODO: Crea Formatter<bool> y da formato al booleano
// TODO: Crea Formatter<const char*> y da formato a la cadena
// 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 cadenas C: "hello" y "world"
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online