Szablony funkcji
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 64 z 104.
Wyobraź sobie, że piszesz funkcję znajdującą maksimum z dwóch liczb całkowitych, a potem zdajesz sobie sprawę, że potrzebujesz tej samej logiki dla liczb zmiennoprzecinkowych typu double, a następnie także dla ciągów znaków. Bez szablonów musiałbyś napisać niemal identyczne funkcje dla każdego typu. Szablony funkcji rozwiązują ten problem, pozwalając napisać logikę raz i umożliwiając kompilatorowi automatyczne generowanie wersji dla konkretnych typów.
Szablon funkcji używa słowa kluczowego template, po którym następują parametry szablonu ujęte w nawiasy ostrokątne:
template <typename T>
T maximum(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << maximum(5, 3) << std::endl; // Używa wersji dla int
std::cout << maximum(3.14, 2.71) << std::endl; // Używa wersji dla double
std::cout << maximum('a', 'z') << std::endl; // Używa wersji dla char
}Kompilator analizuje każde wywołanie i generuje konkretną funkcję dla danego typu. Ten proces nazywa się instancjonowaniem szablonu. W razie potrzeby możesz też jawnie określić typ:
std::cout << maximum<double>(5, 3.14) << std::endl; // Wymusza wersję doubleSzablony mogą mieć wiele parametrów typów, co umożliwia tworzenie jeszcze bardziej elastycznych projektów:
template <typename T, typename U>
void printPair(T first, U second) {
std::cout << first << ", " << second << std::endl;
}
printPair(42, "hello"); // T=int, U=const char*
printPair(3.14, 100); // T=double, U=intSzablony funkcji zapewniają polimorfizm czasu kompilacji — typ jest określany podczas kompilowania kodu, a nie w czasie wykonywania. Oznacza to brak narzutu w czasie wykonywania w porównaniu z funkcjami wirtualnymi, dzięki czemu szablony doskonale nadają się do generycznego kodu, w którym wydajność ma kluczowe znaczenie.
Wyzwanie
ŁatwyStwórzmy zestaw narzędzi, używając szablonów funkcji do tworzenia wielokrotnego użytku operacji, które działają z dowolnym zgodnym typem. Umieścisz swoje funkcje generyczne w pliku nagłówkowym i pokażesz ich elastyczność w programie głównym.
Utworzysz dwa pliki:
MathUtils.h: Zdefiniuj zbiór szablonów funkcji wykonujących typowe operacje:minimum: funkcja szablonowa, która przyjmuje dwie wartości tego samego typu i zwraca mniejszą z nich.clamp: funkcja szablonowa, która przyjmuje trzy parametry: wartość, dolną granicę i górną granicę. Zwraca wartość ograniczoną do tych granic (zwraca low, jeśli value jest mniejsze od low, high, jeśli value jest większe od high, a w przeciwnym razie zwraca samą wartość).swapValues: funkcja szablonowa, która przyjmuje dwie referencje tego samego typu i zamienia ich wartości.main.cpp: Wczytaj sześć danych wejściowych (każdą w osobnym wierszu):- Pierwsza liczba całkowita
- Druga liczba całkowita
- Wartość typu double do ograniczenia
- Dolna granica (double)
- Górna granica (double)
- Znak
Zademonstruj działanie szablonów, wykonując następujące czynności:
- Znajdź minimum z dwóch liczb całkowitych i wyświetl:
Min of <a> and <b>: <result> - Znajdź minimum ze znaków
'm'i wczytanego znaku, wyświetlając:Min of m and <char>: <result> - Ogranicz wartość typu double i wyświetl:
Clamp <value> to [<low>, <high>]: <result> - Ogranicz pierwszą liczbę całkowitą do zakresu [0, 100] i wyświetl:
Clamp <value> to [0, 100]: <result> - Zamień dwie liczby całkowite i wyświetl:
After swapValues: <a>, <b>
Na przykład dla danych wejściowych 25, 10, 3.7, 1.0, 5.0 i z:
Min of 25 and 10: 10
Min of m and z: m
Clamp 3.7 to [1, 5]: 3.7
Clamp 25 to [0, 100]: 25
After swapValues: 10, 25Zwróć uwagę, jak każdy szablon funkcji działa bezproblemowo z liczbami całkowitymi, wartościami typu double i znakami: kompilator generuje odpowiednią wersję dla każdego używanego typu. Funkcja swapValues powinna modyfikować oryginalne zmienne za pośrednictwem referencji, pokazując, że szablony działają z parametrami będącymi referencjami tak samo jak zwykłe funkcje.
Spróbuj swoich sił
#include <iostream>
#include "MathUtils.h"
using namespace std;
int main() {
// Wczytaj dane wejściowe
int a, b;
double value, low, high;
char ch;
cin >> a;
cin >> b;
cin >> value;
cin >> low;
cin >> high;
cin >> ch;
// TODO: Użyj szablonu minimum, aby znaleźć mniejszą z dwóch liczb całkowitych
// Wypisz: "Min of <a> and <b>: <result>"
// TODO: Użyj szablonu minimum, aby znaleźć mniejszą z wartości 'm' i wczytanego znaku
// Wypisz: "Min of m and <char>: <result>"
// TODO: Użyj szablonu clamp dla wartości typu double
// Wypisz: "Clamp <value> to [<low>, <high>]: <result>"
// TODO: Użyj szablonu clamp dla pierwszej liczby całkowitej z zakresem [0, 100]
// Wypisz: "Clamp <value> to [0, 100]: <result>"
// TODO: Użyj szablonu swapValues dla dwóch liczb całkowitych
// Wypisz: "After swapValues: <a>, <b>"
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