Specializace částečné šablony - Partial template specialization

Specializace částečných šablon je zvláštní forma specializace šablon tříd . Obvykle se používá v odkazu na programovací jazyk C ++ a umožňuje programátorovi specializovat pouze některé argumenty šablony třídy, na rozdíl od explicitní plné specializace, kde jsou k dispozici všechny argumenty šablony.

Šablony a specializace

Šablony tříd jsou opravdu meta-třídy: jsou to částečné abstraktní datové typy, které poskytují kompilátoru pokyny, jak vytvořit třídy se správnými datovými členy. Například standardní kontejnery C ++ jsou šablony tříd. Když programátor používá vektor, vytvoří jej instanci konkrétního datového typu, například int, string nebo double. Každý typ vektoru má za následek jinou třídu v objektovém kódu kompilátoru, přičemž každý pracuje s jiným datovým typem. Tento proces se nazývá monomorfizace generik.

Pokud někdo ví, že šablona třídy bude s konkrétním datovým typem používána poměrně často a tento datový typ umožňuje některé optimalizace (např. Posunutí bitů s celými čísly, na rozdíl od násobení nebo dělení 2), lze zavést specializovanou šablonu třídy s některými přednastavených parametrů šablony. Když kompilátor vidí takovou šablonu třídy vytvořenou v kódu, obecně zvolí nejšpecializovanější definici šablony, která odpovídá instanci. Proto bude upřednostněna explicitní úplná specializace (jedna, kde jsou zadány všechny argumenty šablony) před částečnou specializací, pokud se shodují všechny argumenty šablony.

Částečná specializace

Šablony mohou mít více než jeden typ parametru. Některé starší překladače umožňují pouze jednomu specializovat buď všechny, nebo žádný z parametrů šablony. Kompilátory, které podporují částečnou specializaci, umožňují programátorovi specializovat některé parametry a ostatní ponechat obecné.

Příklad

Předpokládejme, že existuje KeyValuePairtřída se dvěma parametry šablony, jak je uvedeno níže.

template <typename Key, typename Value>
class KeyValuePair {};

Následuje příklad třídy, která definuje explicitní specializaci plné šablony KeyValuePairpomocí párování celých čísel s řetězci. Typ třídy si zachovává stejný název jako původní verze.

template <>
class KeyValuePair<int, std::string> {};

Další je příklad částečné specializace KeyValuePairse stejným názvem jako původní verze a jeden specializovaný parametr šablony.

template <typename Key>
class KeyValuePair<Key, std::string> {};

Další příkladová třída KeyStringPairje odvozena od originálu KeyValuePairs novým názvem a definuje částečnou specializaci šablony. Na rozdíl od výslovné specializaci výše, pouze hodnota šablona parametr nadtřídy se specializuje, zatímco Key parametr šablony zůstává obecný.

template <typename Key>
class KeyStringPair : public KeyValuePair<Key, std::string> {};

Nezáleží na tom, které parametry šablony jsou specializované a které zůstávají obecné. Například následující je také platný příklad částečné specializace původní KeyValuePairtřídy.

template <typename Value>
class IntegerValuePair : public KeyValuePair<int, Value> {};

Upozornění

Šablony C ++ se neomezují pouze na třídy - lze je také použít k definování funkčních šablon . Ačkoli šablony funkcí mohou být plně specializované, nemohou být částečně specializované, bez ohledu na to, zda se jedná o šablony členských funkcí nebo šablony funkcí, které nejsou členy. To může být výhodné pro autory kompilátorů, ale ovlivňuje to flexibilitu a granularitu toho, co mohou vývojáři dělat. Šablony funkcí však mohou být přetíženy , což dává téměř stejný účinek jako to, co by měla specializace šablony částečných funkcí. Následující příklady jsou uvedeny pro ilustraci těchto bodů.

// legal: base function template
template <typename ReturnType, typename ArgumentType>
ReturnType Foo(ArgumentType arg);

// legal: explicit/full function template specialization
template <>
std::string Foo<std::string, char>(char arg) { return "Full"; }

// illegal: partial function template specialization of the return type
//          function template partial specialization is not allowed
// template <typename ArgumentType>
// void Foo<void, ArgumentType>(ArgumentType arg);

// legal: overloads the base template for a pointer argument type
template <typename ReturnType, typename ArgumentType>
ReturnType Foo(ArgumentType *argPtr) { return "PtrOverload"; }

// legal: base function name reused. Not considered an overload. ill-formed: non-overloadable declaration (see below)
template <typename ArgumentType>
std::string Foo(ArgumentType arg) { return "Return1"; }

// legal: base function name reused. Not considered an overload. ill-formed: non-overloadable declaration (see below)
template <typename ReturnType>
ReturnType Foo(char arg) { return "Return2"; }

V příkladu uvedeném výše si všimněte, že zatímco poslední dvě definice funkce Foojsou legální C ++, jsou podle standardu považovány za nesprávně vytvořené, protože se nejedná o přetížitelná prohlášení. Důvodem je, že definice přetížení funkce zohledňuje pouze název funkce, seznam typů parametrů a obklopující jmenný prostor (pokud existuje). Nezohledňuje návratový typ. Tyto funkce však lze stále volat explicitním uvedením podpisu kompilátoru, jak ukazuje následující program.

// note: to be compiled in conjunction with the definitions of Foo above

int main(int argc, char *argv[])
{
    char c = 'c';
    std::string r0, r1, r2, r3;
    // let the compiler resolve the call
    r0 = Foo(c);
    // explicitly specify which function to call
    r1 = Foo<std::string>(c);
    r2 = Foo<std::string, char>(c);
    r3 = Foo<std::string, char>(&c);
    // generate output
    std::cout << r0 << " " << r1 << " " << r2 << " " << r3 << std::endl;
    return 0;
}

//expected output:
Return1 Return2 Full PtrOverload

Reference