Variadic šablona - Variadic template
v programování, variadic šablony jsou šablony které berou proměnlivý počet argumentů.
Variadic šablony jsou podporovány C ++ (od C ++ 11 standard) a D programovací jazyk.
C ++
Funkce variadic template v C ++ byla navržena Douglasem Gregorem a Jaakko Järvi [1][2] a později byl standardizován v C ++ 11. Před C ++ 11 mohly šablony (třídy a funkce) trvat pouze pevný počet argumentů, které musely být zadány při první deklaraci šablony. C ++ 11 umožňuje definicím šablon převzít libovolný počet argumentů libovolného typu.
šablona<typename... Hodnoty> třída n-tice; // trvá nula nebo více argumentů
Výše uvedená třída šablony n-tice
vezme jako parametry šablony libovolný počet názvů typů. Zde je instance výše uvedené třídy šablony vytvořena pomocí tří argumentů typu:
n-tice<int, std::vektor<int>, std::mapa<std::tětiva, std::vektor<int>>> some_instance_name;
Počet argumentů může být nula, takže n-tice<> some_instance_name;
bude také fungovat.
Pokud by variadická šablona měla umožňovat pouze kladný počet argumentů, lze použít tuto definici:
šablona<typename První, typename... Odpočinek> třída n-tice; // vezme jeden nebo více argumentů
Variadic šablony se mohou vztahovat také na funkce, a to nejen poskytuje typově bezpečný doplněk k variadickým funkcím (například printf), ale také umožňuje funkci nazvanou syntaxí podobnou printf zpracovávat netriviální objekty.
šablona<typename... Params> prázdnota printf(konst std::tětiva &str_format, Params... parametry);
The elipsa (...) operátor má dvě role. Když k němu dojde nalevo od názvu parametru, deklaruje balíček parametrů. Pomocí sady parametrů může uživatel vázat nula nebo více argumentů k parametrům šablony variadic. Pro netypové parametry lze také použít sady parametrů. Naproti tomu, když se operátor elipsy objeví napravo od argumentu volání šablony nebo funkce, rozbalí balíčky parametrů do samostatných argumentů, například args ...
v těle printf
níže. V praxi použití operátoru elipsy v kódu způsobí, že se celý výraz, který předchází elipsu, opakuje pro každý následující argument rozbalený z balíčku argumentů, přičemž výrazy jsou odděleny čárkami.
Použití variadic šablon je často rekurzivní. Samotné variadické parametry nejsou snadno dostupné pro implementaci funkce nebo třídy. Proto je typický mechanismus pro definování něčeho jako v ++ C ++ 11 printf
náhrada by byla následující:
// základní případprázdnota printf(konst char *s){ zatímco (*s) { -li (*s == '%') { -li (*(s + 1) != '%') ++s; jiný házet std::runtime_error("řetězec neplatného formátu: chybějící argumenty"); } std::cout << *s++; }}// rekurzivníšablona<typename T, typename... Args>prázdnota printf(konst char *s, T hodnota, Args... args){ zatímco (*s) { -li (*s == '%') { -li (*(s + 1) != '%') { std::cout << hodnota; s += 2; // funguje pouze na řetězce ve 2 znacích (% d,% f atd.); selže s% 5.4f printf(s, args...); // zavolá, i když * s je 0, ale v tom případě nedělá nic (a ignoruje další argumenty) vrátit se; } ++s; } std::cout << *s++; } }
Toto je rekurzivní šablona. Všimněte si, že verze variadic šablony printf
volá sám, nebo (v případě, že args ...
je prázdný) volá základní případ.
Neexistuje žádný jednoduchý mechanismus pro iteraci nad hodnotami šablony variadic. Existuje však několik způsobů, jak přeložit balíček argumentů do jednoho argumentu, který lze vyhodnotit samostatně pro každý parametr. Obvykle se to bude spoléhat na přetížení funkce, nebo - pokud funkce může jednoduše vybrat jeden argument najednou - pomocí značky hloupého rozšíření:
šablona<typename... Args> v souladu prázdnota složit(Args&&...) {}
které lze použít následovně:
šablona<typename... Args> v souladu prázdnota rozšířit(Args&&... args) { složit( some_function(args)... ); } rozšířit(42, "Odpovědět", skutečný);
který se rozšíří na něco jako:
složit( some_function(arg1), some_function(arg2), some_function(arg3) atd... );
Použití této funkce „pass“ je nezbytné, protože rozšíření sady argumentů probíhá oddělením argumentů volání funkcí čárkami, které nejsou ekvivalentní operátoru čárky. Proto, some_function (args) ...;
nikdy nebude fungovat. Kromě toho výše uvedené řešení bude fungovat pouze v případě, že je návratový typ some_function
není prázdnota
. Kromě toho some_function
volání budou prováděna v neurčeném pořadí, protože pořadí vyhodnocení argumentů funkce není definováno. Abyste se vyhnuli neurčenému pořadí, lze použít seznamy inicializátorů uzavřených v závorkách, které zaručují přísné pořadí vyhodnocení zleva doprava. Seznam inicializátorů vyžadujeprázdnota
návratový typ, ale k získání lze použít operátor čárky 1
pro každý dilatační prvek.
struktur složit { šablona<typename ...T> složit(T...) {} }; složit{(some_function(args), 1)...};
Místo provádění funkce může být zadán a spuštěn výraz lambda na místě, což umožňuje provádění libovolných sekvencí příkazů na místě.
pass {([&] () {std :: cout << args << std :: endl;} (), 1) ...};
V tomto konkrétním příkladu však funkce lambda není nutná. Místo toho lze použít běžnější výraz:
pass {(std :: cout << args << std :: endl, 1) ...};
Dalším způsobem je použití přetížení u „ukončovacích verzí“ funkcí. To je univerzálnější, ale vyžaduje to trochu více kódu a větší úsilí k vytvoření. Jedna funkce přijímá jeden argument nějakého typu a balíček argumentů, zatímco druhý neobdrží ani jeden. (Pokud by oba měli stejný seznam počátečních parametrů, bylo by volání nejednoznačné - samotný balíček parametrů variadic nemůže disambiguovat hovor.) Například:
prázdnota func() {} // ukončovací verzešablona<typename Arg1, typename... Args>prázdnota func(konst Arg1& arg1, konst Args&&... args){ proces( arg1 ); func(args...); // poznámka: arg1 se zde neobjevuje!}
Li args ...
obsahuje alespoň jeden argument, přesměruje se na druhou verzi - balíček parametrů může být prázdný, v takovém případě se jednoduše přesměruje na ukončovací verzi, která nic neudělá.
Variadic šablony lze také použít ve specifikaci výjimky, seznamu základních tříd nebo inicializačním seznamu konstruktoru. Třída může například zadat následující:
šablona <typename... BaseClasses>třída Jméno třídy : veřejnost BaseClasses...{veřejnost: Jméno třídy (BaseClasses&&... základní_třídy) : BaseClasses(základní_třídy)... {}};
Operátor rozbalení bude replikovat typy pro základní třídy Jméno třídy
, takže tato třída bude odvozena z každého typu předaného. Konstruktor také musí vzít odkaz na každou základní třídu, aby inicializoval základní třídy Jméno třídy
.
Pokud jde o funkční šablony, lze předávat variadické parametry. V kombinaci s univerzálními odkazy (viz výše) to umožňuje dokonalé přeposílání:
šablona<typename TypeToConstruct>struktur SharedPtrAllocator{ šablona<typename ...Args> std::shared_ptr<TypeToConstruct> construct_with_shared_ptr(Args&&... parametry) { vrátit se std::shared_ptr<TypeToConstruct>(Nový TypeToConstruct(std::vpřed<Args>(parametry)...)); }};
Tím se rozbalí seznam argumentů do konstruktoru TypeToConstruct. The std :: forward
syntaxe dokonale předává argumenty jako jejich správné typy, a to i s ohledem na rvalue-ness, konstruktoru. Operátor rozbalení rozšíří syntaxi přeposílání na každý parametr. Tato konkrétní tovární funkce automaticky zabalí přidělenou paměť do a std :: shared_ptr
pro jistotu ohledně úniku paměti.
Počet argumentů v sadě parametrů šablony lze navíc určit takto:
šablona<typename ...Args>struktur SomeStruct{ statický konst int velikost = velikost...(Args);};
Výraz SomeStruct
přinese 2, zatímco SomeStruct <> :: size
dá 0.
D
Definice
Definice variadic šablon v D je podobná jejich protějšku C ++:
šablona VariadicTemplate(Args...) { / * Tělo * / }
Seznam argumentů může předcházet jakýkoli argument:
šablona VariadicTemplate(T, tětiva hodnota, alias symbol, Args...) { / * Tělo * / }
Základní použití
Variadic argumenty jsou velmi podobné konstantnímu poli v jejich použití. Mohou být iterovány, přístupné indexem, mají a délka
majetku a může být nakrájený. Operace jsou interpretovány v době kompilace, což znamená, že operandům nemůže být runtime hodnota (například funkční parametry).
Cokoli, co je známo v době kompilace, lze předat jako variadic argumenty. Dělá variadic argumenty podobné argumenty aliasu šablony, ale výkonnější, protože přijímají i základní typy (char, short, int ...).
Zde je příklad, který tiskne řetězovou reprezentaci variadic parametrů. StringOf
a StringOf2
dosáhnout stejných výsledků.
statický int s_int;struktur Dummy {}prázdnota hlavní(){ pragma(zpráva, StringOf!("Ahoj světe", uint, Dummy, 42, s_int)); pragma(zpráva, StringOf2!("Ahoj světe", uint, Dummy, 42, s_int));}šablona StringOf(Args...){ výčet StringOf = Args[0].řetězec ~ StringOf!(Args[1..$]);}šablona StringOf(){ výčet StringOf = "";}šablona StringOf2(Args...){ statický -li (Args.délka == 0) výčet StringOf2 = ""; jiný výčet StringOf2 = Args[0].řetězec ~ StringOf2!(Args[1..$]);}
Výstupy:
"Hello world" uintDummy42s_int "Hello world" uintDummy42s_int
AliasSeq
Variadic šablony se často používají k vytvoření posloupnosti aliasů s názvem AliasSeq Definice AliasSeq je ve skutečnosti velmi přímá:
alias AliasSeq(Args...) = Args;
Tato struktura umožňuje manipulovat se seznamem variadic argumentů, které se automaticky rozbalí. Argumenty musí být buď symboly, nebo hodnoty známé v době kompilace. To zahrnuje hodnoty, typy, funkce nebo dokonce nespecializované šablony. To umožňuje jakoukoli operaci, kterou byste očekávali:
import std.meta;prázdnota hlavní(){ // Poznámka: AliasSeq nelze upravit a alias nelze odskočit, takže pro naše úpravy budeme muset definovat nové názvy. alias čísla = AliasSeq!(1, 2, 3, 4, 5, 6); // krájení alias poslední = čísla[$ / 2 .. $]; statický tvrdit(poslední == AliasSeq!(4, 5, 6)); // AliasSeq auto expanze alias číslice = AliasSeq!(0, čísla, 7, 8, 9); statický tvrdit(číslice == AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); // std.meta poskytuje šablony pro práci s AliasSeq, jako jsou anySatisfy, allSatisfy, staticMap a Filter. alias sudá čísla = Filtr!(isEven, číslice); statický tvrdit(sudá čísla == AliasSeq!(0, 2, 4, 6, 8));}šablona isEven(int číslo){ výčet isEven = (0 == (číslo % 2));}
Viz také
Pro články o variadických konstrukcích jiných než šablony
Reference
- ^ Douglas Gregor & Jaakko Järvi (únor 2008). „Variadic Templates for C ++ 0x“. Journal of Object Technology. 31–51.
- ^ Douglas Gregor; Jaakko Järvi a Gary Powell. (Únor 2004). „Variadic templates. Number N1603 = 04-0043 in ISO C ++ Standard Committee Pre-Sydney mailing“.