Szablon (C++)
Szablony to funkcja języka programowania C++ , która umożliwia funkcjom i klasom działanie z typami ogólnymi . Dzięki temu funkcja lub klasa może pracować na wielu różnych typach danych bez konieczności przepisywania każdego z nich.
Biblioteka standardowa C++ zapewnia wiele przydatnych funkcji w ramach połączonych szablonów.
Główną inspiracją dla szablonów C++ były sparametryzowane moduły dostarczone przez CLU oraz generyczne dostarczone przez Adę .
Przegląd techniczny
Istnieją trzy rodzaje szablonów: szablony funkcji , szablony klas i od C++14 szablony zmiennych . Od C++11 szablony mogą być zmienne lub niezmienne; we wcześniejszych wersjach C++ są one zawsze niewariacyjne.
Szablony funkcji
Szablon funkcji zachowuje się jak funkcja, z tą różnicą, że szablon może mieć argumenty wielu różnych typów (patrz przykład). Innymi słowy, szablon funkcji reprezentuje rodzinę funkcji. Format deklarowania szablonów funkcji z parametrami typu jest następujący:
szablon < identyfikator klasy > deklaracja ; szablon < identyfikator nazwy typu > deklaracja ;
Oba wyrażenia mają to samo znaczenie i zachowują się dokładnie w ten sam sposób. Ta ostatnia forma została wprowadzona, aby uniknąć nieporozumień, ponieważ parametr typu nie musi być klasą aż do C++20. (Może to być typ podstawowy, taki jak int
lub double
.)
Na przykład standardowa biblioteka języka C++ zawiera szablon funkcji max(x, y)
, który zwraca większą z wartości x
i y
. Ten szablon funkcji można zdefiniować w następujący sposób:
szablon < nazwa typu T > T max ( T & a , T & b ) { return a > b ? za : b ; }
Ta pojedyncza definicja funkcji działa z wieloma typami danych. W szczególności działa ze wszystkimi typami danych, dla których > (operator większy niż). Użycie szablonu funkcji oszczędza miejsce w pliku kodu źródłowego, ogranicza zmiany do jednego opisu funkcji i ułatwia czytanie kodu.
Szablon nie tworzy jednak mniejszego kodu obiektowego w porównaniu z pisaniem oddzielnych funkcji dla wszystkich różnych typów danych używanych w określonym programie. Na przykład, jeśli program używa zarówno int
, jak i double
wersji szablonu funkcji max()
pokazanego powyżej, kompilator utworzy wersję kodu obiektowego max()
operującą na argumentach int
oraz inną wersję kodu obiektowego, która operuje na double
argumenty. Dane wyjściowe kompilatora będą identyczne z tymi, które zostałyby wygenerowane, gdyby kod źródłowy zawierał dwie oddzielne wersje max()
bez szablonu , jedną napisaną do obsługi int
i jedną do obsługi double
.
Oto jak można użyć szablonu funkcji:
#include <iostream> int main () { // Spowoduje to wywołanie max<int> przez niejawną dedukcję argumentów. std :: cout << max ( 3 , 7 ) << '\n' ; // Spowoduje to wywołanie max<double> poprzez odliczenie niejawnego argumentu. std :: cout << maks ( 3.0 , 7.0 ) << '\n' ;
// Musimy jawnie określić typ argumentów; // chociaż std::type_identity mogłoby rozwiązać ten problem... std :: cout << max < double > ( 3 , 7.0 ) << '\n' ; }
W pierwszych dwóch przypadkach argument szablonu T
jest automatycznie dedukowany przez kompilator jako odpowiednio int
i double
. W trzecim przypadku automatyczne odliczenie max(3, 7.0)
nie powiedzie się, ponieważ typ parametrów musi na ogół dokładnie odpowiadać argumentom szablonu. Dlatego jawnie tworzymy instancję podwójnej
wersji za pomocą max<double>()
.
Ten szablon funkcji można utworzyć za pomocą dowolnego typu konstruowanego przez kopiowanie, dla którego wyrażenie y > x
jest prawidłowe. W przypadku typów zdefiniowanych przez użytkownika oznacza to, że operator większy niż ( >
) musi być przeciążony w typie.
Szablony klas
Szablon klasy zawiera specyfikację generowania klas na podstawie parametrów. Szablony klas są zwykle używane do implementacji kontenerów . Szablon klasy jest tworzony przez przekazanie mu określonego zestawu typów jako argumentów szablonu. Biblioteka standardowa C++ zawiera wiele szablonów klas, w szczególności kontenery zaadaptowane ze standardowej biblioteki szablonów , takie jak vector
.
Zmienne szablony
W C++14 szablony mogą być również używane dla zmiennych, jak w poniższym przykładzie:
szablon < nazwa typu T > constexpr T pi = T { 3.141592653589793238462643383L }; // (prawie) ze std::numbers::pi
Nietypowe parametry szablonu
Chociaż tworzenie szablonów na typach, jak w powyższych przykładach, jest najpowszechniejszą formą tworzenia szablonów w C++, możliwe jest również tworzenie szablonów na wartościach. Tak więc na przykład klasa zadeklarowana za pomocą
szablon < int K > klasa MojaKlasa ;
można utworzyć instancję za pomocą określonego int
.
Jako rzeczywisty przykład, standardowa biblioteka typu tablicy o stałym rozmiarze std::array
jest wzorowana zarówno na typie (reprezentującym typ obiektu, który przechowuje tablica), jak i liczbie, która jest typu std::size_t
(reprezentująca liczba elementów przechowywanych w tablicy). std::array
można zadeklarować w następujący sposób:
szablon < klasa T , rozmiar_t N > tablica struktur ;
można zadeklarować tablicę sześciu znaków :
tablica < znak , 6 > moja tablica ;
Specjalizacja szablonu
Kiedy instancja funkcji lub klasy jest tworzona z szablonu, kompilator tworzy specjalizację tego szablonu dla zestawu użytych argumentów, a specjalizacja jest nazywana specjalizacją wygenerowaną.
Jawna specjalizacja szablonu
Czasami programista może zdecydować się na zaimplementowanie specjalnej wersji funkcji (lub klasy) dla danego zestawu argumentów typu szablonu, co nazywa się jawną specjalizacją. W ten sposób niektóre typy szablonów mogą mieć wyspecjalizowaną implementację, która jest zoptymalizowana pod kątem typu lub bardziej znaczącą implementację niż implementacja ogólna.
- Jeśli szablon klasy jest wyspecjalizowany przez podzbiór jego parametrów, nazywa się to częściową specjalizacją szablonu (szablony funkcji nie mogą być częściowo wyspecjalizowane).
- Jeśli wszystkie parametry są wyspecjalizowane, jest to pełna specjalizacja .
Jawna specjalizacja jest używana, gdy zachowanie funkcji lub klasy dla określonych wyborów parametrów szablonu musi odbiegać od ogólnego zachowania, czyli od kodu generowanego przez główny szablon lub szablony. Na przykład poniższa definicja szablonu definiuje konkretną implementację max()
dla argumentów typu const char*
:
0 #include <cstring> template <> const char * max ( const char * a , const char * b ) { // Zwykle wynikiem bezpośredniego porównania // dwóch łańcuchów C jest niezdefiniowane zachowanie; // użycie std::strcmp definiuje. powrót std :: strcmp ( a , b ) > ? za : b ;
}
Wariadyczne szablony
W C++11 wprowadzono szablony wariacyjne , które mogą przyjmować zmienną liczbę argumentów w sposób nieco podobny do funkcji wariacyjnych , takich jak std::printf
.
Aliasy szablonów
C++11 wprowadził aliasy szablonów, które działają jak sparametryzowane definicje typów .
Poniższy kod przedstawia definicję aliasu szablonu StrMap
. Pozwala to na przykład na użycie StrMap<int> jako skrótu dla
std::unordered_map<int,std::string>
.
szablon < nazwa typu T > przy użyciu StrMap = std :: unordered_map < T , std :: string > ;
Ogólne funkcje programowania w innych językach
Początkowo koncepcja szablonów nie była uwzględniana w niektórych językach, takich jak Java i C# 1.0. Przyjęcie przez Javę typów generycznych naśladuje zachowanie szablonów, ale jest technicznie inne. W języku C# dodano generyczne (typy sparametryzowane) w programie .NET 2,0. Generyki w Adzie są starsze niż szablony C++.
Chociaż szablony języka C++, typy ogólne języka Java i typy generyczne .NET są często uważane za podobne, szablony generyczne naśladują jedynie podstawowe zachowanie szablonów języka C++. Niektóre zaawansowane funkcje szablonów wykorzystywane przez biblioteki, takie jak Boost i STLSoft, oraz implementacje samego STL do metaprogramowania szablonów (specjalizacja jawna lub częściowa, domyślne argumenty szablonu, argumenty szablonu inne niż typ, argumenty szablonu szablonu, ...) są niedostępne z lekami generycznymi.
W szablonach C++ przypadki kompilacji były historycznie wykonywane przez dopasowywanie wzorców do argumentów szablonu. Na przykład klasa bazowa szablonu w poniższym przykładzie czynnikowym jest implementowana przez dopasowanie 0 zamiast testu nierówności, który wcześniej był niedostępny. Jednak pojawienie się w C++ 11 funkcji standardowej biblioteki, takich jak std::conditional, zapewniło inny, bardziej elastyczny sposób obsługi tworzenia instancji szablonów warunkowych.
0
// Szablon indukcyjny < unsigned N > struct Factorial { static constexpr unsigned value = N * Factorial < N - 1 >:: value ; }; // Przypadek podstawowy poprzez specjalizację szablonu: template <> struct Factorial < > { static constexpr unsigned value = 1
; };
Dzięki tym definicjom można obliczyć, powiedzmy 6! w czasie kompilacji przy użyciu wyrażenia Factorial<6>::value
. Alternatywnie constexpr
w C++11 / consteval
w C++20 może służyć do bezpośredniego obliczania takich wartości przy użyciu funkcji w czasie kompilacji. Z tego powodu metaprogramowanie szablonów jest obecnie najczęściej używane do wykonywania operacji na typach.
Zobacz też
- Metaprogramowanie szablonów
- Metaprogramowanie
- Monomorfizacja
- Programowanie ogólne
- Tylko nagłówek
- Niepowodzenie zamiany nie jest błędem
- Ciekawie powtarzający się wzór szablonu
- Lista bibliotek szablonów C++
Linki zewnętrzne
- Demonstracja kompletności Turinga szablonów C++ (implementacja rachunku Lambda)