Šablony (C++)
Šablony jsou základem obecného programování v jazyce C++. Jazyk C++ jako jazyk silného typu vyžaduje, aby všechny proměnné měly konkrétní typ, a to buď explicitně deklarovaný programátorem, nebo vyvolaný kompilátorem. Mnoho datových struktur a algoritmů ale vypadá stejně bez ohledu na to, na jakém typu pracují. Šablony umožňují definovat operace třídy nebo funkce a umožnit uživateli určit konkrétní typy těchto operací.
Definování a používání šablon
Šablona je konstrukce, která vygeneruje běžný typ nebo funkci v době kompilace na základě argumentů, které uživatel poskytuje pro parametry šablony. Můžete například definovat šablonu funkce takto:
template <typename T>
T minimum(const T& lhs, const T& rhs)
{
return lhs < rhs ? lhs : rhs;
}
Výše uvedený kód popisuje šablonu obecné funkce s jedním parametrem typu T, jehož návratová hodnota a parametry volání (lhs a rhs) jsou všechny tohoto typu. Parametr typu můžete pojmenovat cokoliv, co se vám líbí, ale nejčastěji se používají velká písmena s jedním velkými písmeny podle konvence. T je parametr šablony; typename
klíčové slovo říká, že tento parametr je zástupný symbol pro typ. Při zavolání funkce kompilátor nahradí každou instanci konkrétního T
typu argument, který je buď určen uživatelem, nebo vyvolá kompilátor. Proces, při kterém kompilátor generuje třídu nebo funkci ze šablony, se označuje jako vytvoření instance šablony; minimum<int>
je instance šablony minimum<T>
.
Jinde může uživatel deklarovat instanci šablony, která je specializovaná pro int. Předpokládejme, že get_a() a get_b() jsou funkce, které vrací int:
int a = get_a();
int b = get_b();
int i = minimum<int>(a, b);
Vzhledem k tomu, že se jedná o šablonu funkce a kompilátor může odvodit typ T
argumentů a a b, můžete ho volat stejně jako obyčejnou funkci:
int i = minimum(a, b);
Když kompilátor narazí na poslední příkaz, vygeneruje novou funkci, ve které je každý výskyt T v šabloně nahrazen:int
int minimum(const int& lhs, const int& rhs)
{
return lhs < rhs ? lhs : rhs;
}
Pravidla pro způsob, jakým kompilátor provádí odpočty typů v šablonách funkcí, jsou založená na pravidlech pro běžné funkce. Další informace naleznete v tématu Přetížení rozlišení volání šablony funkce.
Parametry typu
minimum
Ve výše uvedené šabloně si všimněte, že parametr typu T není kvalifikován žádným způsobem, dokud se nepoužije v parametrech volání funkce, kde jsou přidány const a referenční kvalifikátory.
Počet parametrů typu není nijak praktický. Více parametrů oddělte čárkami:
template <typename T, typename U, typename V> class Foo{};
Klíčové slovo class
je v tomto kontextu ekvivalentní typename
. Předchozí příklad můžete vyjádřit takto:
template <class T, class U, class V> class Foo{};
Pomocí operátoru tří teček (...) můžete definovat šablonu, která přebírá libovolný počet nulových nebo více parametrů typu:
template<typename... Arguments> class vtclass;
vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
Libovolný předdefinovaný nebo uživatelem definovaný typ lze použít jako argument typu. Můžete například použít std::vector ve standardní knihovně k ukládání proměnných typu int
, double
std ::string, MyClass
* const
MyClass
a MyClass&
tak dále. Primárním omezením při použití šablon je, že argument typu musí podporovat všechny operace použité na parametry typu. Pokud například voláme minimum
jako MyClass
v tomto příkladu:
class MyClass
{
public:
int num;
std::wstring description;
};
int main()
{
MyClass mc1 {1, L"hello"};
MyClass mc2 {2, L"goodbye"};
auto result = minimum(mc1, mc2); // Error! C2678
}
Vygeneruje se chyba kompilátoru, protože MyClass
neposkytuje přetížení operátoru < .
Neexistuje žádný základní požadavek, že argumenty typu pro každou konkrétní šablonu patří do stejné hierarchie objektů, i když můžete definovat šablonu, která takové omezení vynucuje. Můžete kombinovat objektově orientované techniky se šablonami; Můžete například uložit odvozený* v vektorové<bázi*>. Všimněte si, že argumenty musí být ukazatele.
vector<MyClass*> vec;
MyDerived d(3, L"back again", time(0));
vec.push_back(&d);
// or more realistically:
vector<shared_ptr<MyClass>> vec2;
vec2.push_back(make_shared<MyDerived>());
Základní požadavky, které std::vector
a další standardní kontejnery knihoven ukládají na prvky T
, je, že T
je možné přiřadit kopírování a kopírovat-konstruktible.
Parametry bez typu
Na rozdíl od obecných typů v jinýchchm Můžete například zadat celočíselnou hodnotu konstanty, která určuje délku pole, stejně jako v tomto příkladu, který je podobný třídě std::array ve standardní knihovně:
template<typename T, size_t L>
class MyArray
{
T arr[L];
public:
MyArray() { ... }
};
Poznamenejte si syntaxi v deklaraci šablony. Hodnota size_t
je předána jako argument šablony v době kompilace a musí být const
nebo constexpr
výraz. Používáte ho takto:
MyArray<MyClass*, 10> arr;
Jiné typy hodnot, včetně ukazatelů a odkazů, lze předat jako netypové parametry. Můžete například předat ukazatel na funkci nebo objekt funkce a přizpůsobit nějakou operaci uvnitř kódu šablony.
Odpočty typů pro parametry šablony bez typu
V sadě Visual Studio 2017 a novějším a v /std:c++17
režimu nebo novějším kompilátor odvodí typ netypového argumentu šablony deklarovaného pomocí auto
:
template <auto x> constexpr auto constant = x;
auto v1 = constant<5>; // v1 == 5, decltype(v1) is int
auto v2 = constant<true>; // v2 == true, decltype(v2) is bool
auto v3 = constant<'a'>; // v3 == 'a', decltype(v3) is char
Šablony jako parametry šablon
Šablona může být parametrem šablony. V tomto příkladu má MyClass2 dva parametry šablony: parametr typename T a parametr šablony Arr:
template<typename T, template<typename U, int I> class Arr>
class MyClass2
{
T t; //OK
Arr<T, 10> a;
U u; //Error. U not in scope
};
Protože samotný parametr Arr nemá žádný text, jeho názvy parametrů nejsou potřeba. Ve skutečnosti se jedná o chybu odkazující na název typu nebo názvy parametrů třídy Arr z textu MyClass2
souboru . Z tohoto důvodu je možné vynechat názvy parametrů typu Arr, jak je znázorněno v tomto příkladu:
template<typename T, template<typename, int> class Arr>
class MyClass2
{
T t; //OK
Arr<T, 10> a;
};
Výchozí argumenty šablony
Šablony tříd a funkcí můžou mít výchozí argumenty. Pokud má šablona výchozí argument, můžete ho nechat nezadanou, když ji použijete. Například šablona std::vector má výchozí argument pro alokátor:
template <class T, class Allocator = allocator<T>> class vector;
Ve většině případů je přijatelná výchozí třída std::allocator, takže použijete vektor podobný tomuto:
vector<int> myInts;
V případě potřeby ale můžete zadat vlastní alokátor takto:
vector<int, MyAllocator> ints;
U více argumentů šablony musí mít všechny argumenty za prvním výchozím argumentem výchozí argumenty výchozí argumenty.
Při použití šablony, jejíž parametry jsou všechny výchozí, použijte prázdné úhlové závorky:
template<typename A = int, typename B = double>
class Bar
{
//...
};
...
int main()
{
Bar<> bar; // use all default type arguments
}
Specializace šablon
V některých případech není možné ani žádoucí, aby šablona definovala přesně stejný kód pro libovolný typ. Například můžete chtít definovat cestu kódu, která se má provést pouze v případě, že argument typu je ukazatel, nebo std::wstring, nebo typ odvozený z konkrétní základní třídy. V takových případech můžete definovat specializaci šablony pro daný typ. Když uživatel vytvoří instanci šablony s tímto typem, kompilátor použije specializaci ke generování třídy a pro všechny ostatní typy kompilátor zvolí obecnější šablonu. Specializace, ve kterých jsou všechny parametry specializované, jsou kompletní specializace. Pokud jsou specializované jenom některé parametry, nazývá se částečná specializace.
template <typename K, typename V>
class MyMap{/*...*/};
// partial specialization for string keys
template<typename V>
class MyMap<string, V> {/*...*/};
...
MyMap<int, MyClass> classes; // uses original template
MyMap<string, MyClass> classes2; // uses the partial specialization
Šablona může mít libovolný počet specializace, pokud je každý specializovaný parametr typu jedinečný. Pouze šablony tříd můžou být částečně specializované. Všechny úplné a částečné specializace šablony musí být deklarovány ve stejném oboru názvů jako původní šablona.
Další informace naleznete v tématu Specializace podle šablon.
Váš názor
https://aka.ms/ContentUserFeedback.
Připravujeme: V průběhu roku 2024 budeme postupně vyřazovat problémy z GitHub coby mechanismus zpětné vazby pro obsah a nahrazovat ho novým systémem zpětné vazby. Další informace naleznete v tématu:Odeslat a zobrazit názory pro