Dobra robota. Szablony funkcji mogą wydawać się dość skomplikowane, ale stanowią bardzo skuteczny sposób na umożliwienie działania kodu z obiektami różnych typów. W przyszłych rozdziałach zobaczymy znacznie więcej szablonów, więc trzymaj kapelusz.
Przegląd rozdziału
Przeciążanie funkcji pozwala nam tworzyć wiele funkcji o tej samej nazwie, pod warunkiem, że każda funkcja o identycznej nazwie ma inny zestaw typów parametrów (lub funkcje można w inny sposób różnicować). Taka funkcja nazywana jest funkcją przeciążoną (lub przeciążeniem w skrócie). Zwracane typy nie są brane pod uwagę przy różnicowaniu.
Jeśli podczas rozwiązywania przeciążonych funkcji nie zostanie znalezione dokładne dopasowanie, kompilator będzie faworyzował przeciążone funkcje, które można dopasować za pomocą promocji numerycznych, zamiast tych, które wymagają konwersji numerycznych. Kiedy wykonywane jest wywołanie funkcji do funkcji, która została przeciążona, kompilator spróbuje dopasować wywołanie funkcji do odpowiedniego przeciążenia w oparciu o argumenty użyte w wywołaniu funkcji. Nazywa się to rozwiązywaniem przeciążenia.
An dopasowaniem niejednoznacznym występuje, gdy kompilator znajdzie dwie lub więcej funkcji, które mogą dopasować wywołanie funkcji do przeciążonej funkcji i nie może określić, która z nich jest najlepsza.
A argument domyślny to wartość domyślna podana dla parametru funkcji. Parametry z argumentami domyślnymi muszą zawsze być parametrami skrajnymi na prawo i nie są używane do różnicowania funkcji podczas rozwiązywania przeciążonych funkcji.
Szablony funkcji pozwalają nam stworzyć definicję przypominającą funkcję, która służy jako wzorzec do tworzenia powiązanych funkcji. W szablonie funkcji używamy parametrów szablonu typu jako symboli zastępczych dla dowolnych typów, które chcemy później określić. Składnia, która informuje kompilator, że definiujemy szablon i deklaruje typy szablonów, nazywa się deklaracją parametrów szablonu.
Proces tworzenia funkcji (z określonymi typami) z szablonów funkcji (z typami szablonów) nazywa się w skrócie instancją szablonu funkcji (lub instancją). Kiedy ten proces zachodzi w wyniku wywołania funkcji, nazywa się to niejawną instancją. Utworzona instancja funkcji nazywana jest w skrócie instancją funkcji (lub instancją lub czasami funkcją szablonową).
dedukcją argumentów szablonu pozwala kompilatorowi wydedukować rzeczywisty typ, który powinien zostać użyty do utworzenia instancji funkcji na podstawie argumentów wywołania funkcji. Dedukcja argumentów szablonu nie powoduje konwersji typów.
Typy szablonów są czasami nazywane typami ogólnymi, a programowanie przy użyciu szablonów jest czasami nazywane programowaniem ogólnym.
W C++20, gdy słowo kluczowe auto jest używane jako typ parametru w normalnej funkcji, kompilator automatycznie konwertuje funkcję na szablon funkcji, przy czym każdy parametr auto staje się niezależny parametr typu szablonu. Ta metoda tworzenia szablonu funkcji nazywana jest skróconym szablonem funkcji.
A nietypowym parametrem szablonu jest parametrem szablonu o stałym typie, który służy jako symbol zastępczy wartości constexpr przekazanej jako argument szablonu.
Czas quizu
Pytanie nr 1
1a) Jakie jest wyjście tego programu i dlaczego?
#include <iostream>
void print(int x)
{
std::cout << "int " << x << '\n';
}
void print(double x)
{
std::cout << "double " << x << '\n';
}
int main()
{
short s { 5 };
print(s);
return 0;
}>1b) Dlaczego poniższe polecenie się nie skompiluje?
#include <iostream>
void print()
{
std::cout << "void\n";
}
void print(int x=0)
{
std::cout << "int " << x << '\n';
}
void print(double x)
{
std::cout << "double " << x << '\n';
}
int main()
{
print(5.0f);
print();
return 0;
}1c) Dlaczego poniższe skompilować?
#include <iostream>
void print(long x)
{
std::cout << "long " << x << '\n';
}
void print(double x)
{
std::cout << "double " << x << '\n';
}
int main()
{
print(5);
return 0;
}Pytanie nr 2
> Krok #1
Napisz szablon funkcji o nazwie add() który umożliwi użytkownikom dodanie 2 wartości tego samego typu. Powinien zostać uruchomiony następujący program:
#include <iostream>
// write your add function template here
int main()
{
std::cout << add(2, 3) << '\n';
std::cout << add(1.2, 3.4) << '\n';
return 0;
}i wygenerowany zostanie następujący wynik:
5 4.6
> Krok #2
Napisz szablon funkcji o nazwie
mult() który pozwala użytkownikowi pomnożyć jedną wartość dowolnego typu (pierwszy parametr) i liczbę całkowitą (drugi parametr). Drugi parametr nie powinien być typem szablonu. Funkcja powinna zwracać ten sam typ co pierwszy parametr. Powinien zostać uruchomiony następujący program:#include <iostream>
// write your mult function template here
int main()
{
std::cout << mult(2, 3) << '\n';
std::cout << mult(1.2, 3) << '\n';
return 0;
}i wygenerowany zostanie następujący wynik:
6 3.6
> Krok #3
Napisz szablon funkcji o nazwie sub() który pozwala użytkownikowi odjąć dwie wartości różnych typów. Powinien zostać uruchomiony następujący program:
#include <iostream>
// write your sub function template here
int main()
{
std::cout << sub(3, 2) << '\n';
std::cout << sub(3.5, 2) << '\n';
std::cout << sub(4, 1.5) << '\n';
return 0;
}i wygenerowany zostanie następujący wynik:
1 1.5 2.5
Pytanie nr 3
Jaki jest wynik tego programu i dlaczego?
#include <iostream>
template <typename T>
int count(T) // This is the same as int count(T x), except we're not giving the parameter a name since we don't use the parameter
{
static int c { 0 };
return ++c;
}
int main()
{
std::cout << count(1) << '\n';
std::cout << count(1) << '\n';
std::cout << count(2.3) << '\n';
std::cout << count<double>(1) << '\n';
return 0;
}Pytanie nr 4
Jaki jest wynik tego programu?
#include <iostream>
int foo(int n)
{
return n + 10;
}
template <typename T>
int foo(T n)
{
return n;
}
int main()
{
std::cout << foo(1) << '\n'; // #1
short s { 2 };
std::cout << foo(s) << '\n'; // #2
std::cout << foo<int>(4) << '\n'; // #3
std::cout << foo<int>(s) << '\n'; // #4
std::cout << foo<>(6) << '\n'; // #5
return 0;
}
