Specjalizacja szablonów
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 66 z 104.
Czasami ogólny szablon nie działa dobrze dla każdego typu. Na przykład porównanie ciągów znaków w stylu C za pomocą > porównuje adresy wskaźników, a nie rzeczywisty tekst. Specjalizacja szablonu pozwala podać niestandardową implementację dla określonych typów, zachowując ogólną wersję dla wszystkich pozostałych.
Pełna specjalizacja zastępuje cały szablon dla jednego określonego typu. Deklarujesz ją za pomocą pustej listy parametrów szablonu i określasz konkretny typ:
// Szablon główny
template <typename T>
class Printer {
public:
void print(T value) {
std::cout << value << std::endl;
}
};
// Pełna specjalizacja dla 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); // Wynik: 42
Printer<const char*> strPrinter;
strPrinter.print("hello"); // Wynik: String: helloSzablony funkcji również można specjalizować:
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); // Używa wersji ogólnej
isEqual("hi", "hi"); // Używa wersji wyspecjalizowanejKompilator zawsze wybiera najbardziej szczegółowe dopasowanie — jeśli istnieje specjalizacja dla dokładnie używanego typu, zostanie wybrana zamiast szablonu ogólnego.
Wyzwanie
ŁatwyZbudujmy system formatowania świadomy typów, który pokazuje, jak specjalizacja szablonów pozwala dostosować zachowanie do konkretnych typów, zachowując jednocześnie ogólną wersję domyślną dla wszystkich pozostałych.
Utworzysz dwa pliki, aby uporządkować kod szablonów:
Formatter.h: Zdefiniuj szablon klasy o nazwieFormatter, który formatuje wartości do wyświetlenia. Ogólny szablon powinien działać z dowolnym typem, ale utworzysz też wyspecjalizowane wersje dla typów wymagających niestandardowej obsługi.Podstawowy szablon
Formatterpowinien zawierać:- Metodę
format(), która przyjmuje wartość typu T i wyświetla:Value: <value>
Utwórz pełną specjalizację dla
bool, która wyświetla czytelny dla człowieka tekst zamiast 1 lub 0:- Metoda
format()powinna wyświetlać:Boolean: truelubBoolean: false
Utwórz pełną specjalizację dla
const char*, która dodaje cudzysłowy wokół ciągów znaków:- Metoda
format()powinna wyświetlać:String: "<value>"
Utwórz również szablon funkcji o nazwie
formatPair, który przyjmuje dwie wartości tego samego typu i wyświetla je razem w formacie:Pair: [<first>, <second>]Utwórz specjalizację
formatPairdlaconst char*, która ujmuje każdy ciąg znaków w cudzysłowy:Pair: ["<first>", "<second>"]- Metodę
main.cpp: Wczytaj cztery dane wejściowe (każdą w osobnym wierszu):- Liczbę całkowitą
- Liczbę typu double
- Wartość logiczną jako ciąg znaków (
truelubfalse) - Wartość tekstową
Zademonstruj działanie formaterów, tworząc odpowiednie obiekty
Formatteri wywołującformat()dla każdego typu:- Sformatuj liczbę całkowitą za pomocą
Formatter<int> - Sformatuj liczbę typu double za pomocą
Formatter<double> - Sformatuj wartość logiczną za pomocą
Formatter<bool>(najpierw przekształć wejściowy ciąg znaków na wartość bool) - Sformatuj ciąg znaków za pomocą
Formatter<const char*>
Następnie zademonstruj specjalizację szablonu funkcji:
- Wywołaj
formatPairz dwiema liczbami całkowitymi: 10 i 20 - Wywołaj
formatPairz dwoma ciągami C:"hello"i"world"
Na przykład dla danych wejściowych 42, 3.14, true i Hello:
Value: 42
Value: 3.14
Boolean: true
String: "Hello"
Pair: [10, 20]
Pair: ["hello", "world"]Zwróć uwagę, że ogólny szablon obsługuje liczby całkowite i liczby typu double w identyczny sposób, podczas gdy wyspecjalizowane wersje dla bool i const char* zapewniają niestandardowe formatowanie. Kompilator automatycznie wybiera najbardziej szczegółowe dopasowanie dla każdego użytego typu.
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "Formatter.h"
using namespace std;
int main() {
// Wczytaj dane wejściowe
int intVal;
double doubleVal;
string boolStr;
string strVal;
cin >> intVal;
cin >> doubleVal;
cin >> boolStr;
cin >> strVal;
// Przekonwertuj ciąg znaków na bool
bool boolVal = (boolStr == "true");
// TODO: Utwórz Formatter<int> i sformatuj liczbę całkowitą
// TODO: Utwórz Formatter<double> i sformatuj liczbę typu double
// TODO: Utwórz Formatter<bool> i sformatuj wartość logiczną
// TODO: Utwórz Formatter<const char*> i sformatuj ciąg znaków
// Wskazówka: Użyj strVal.c_str(), aby uzyskać const char*
// TODO: Wywołaj formatPair z dwiema liczbami całkowitymi: 10 i 20
// TODO: Wywołaj formatPair z dwoma ciągami znaków typu C: "hello" i "world"
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online