Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of mappen te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen om mappen te wijzigen.
In dit artikel wordt beschreven hoe u het beletselteken (...) gebruikt met verschillende C++-sjablonen. Het beletselteken heeft veel gebruikt in C en C++. Deze omvatten lijsten met variabelenargumenten voor functies. De printf() functie uit de C Runtime-bibliotheek is een van de bekendste voorbeelden.
Een variantiesjabloon is een klasse- of functiesjabloon die ondersteuning biedt voor een willekeurig aantal argumenten. Dit mechanisme is vooral nuttig voor ontwikkelaars van C++-bibliotheken: u kunt dit toepassen op zowel klassesjablonen als functiesjablonen, en zo een breed scala aan typeveilige en niet-triviale functionaliteit en flexibiliteit bieden.
Syntaxis
Een beletselteken wordt op twee manieren gebruikt door verschillende sjablonen. Links van de parameternaam wordt een parameterpakket opgegeven en rechts van de parameternaam worden de parameterpakketten uitgebreid tot afzonderlijke namen.
Hier volgt een basisvoorbeeld van de syntaxis van de definitie van een variantische klassesjabloon :
template<typename... Arguments> class classname;
Voor zowel parameterpakketten als uitbreidingen kunt u witruimte rond het beletselteken toevoegen, op basis van uw voorkeur, zoals wordt weergegeven in dit voorbeeld:
template<typename ...Arguments> class classname;
Of dit voorbeeld:
template<typename ... Arguments> class classname;
In dit artikel wordt de conventie gebruikt die wordt weergegeven in het eerste voorbeeld (het beletselteken is gekoppeld aan typename).
In de voorgaande voorbeelden Arguments is dit een parameterpakket. De klasse classname kan een variabel aantal argumenten accepteren, zoals in deze voorbeelden:
template<typename... Arguments> class vtclass;
vtclass< > vtinstance1;
vtclass<int> vtinstance2;
vtclass<float, bool> vtinstance3;
vtclass<long, std::vector<int>, std::string> vtinstance4;
Met behulp van een variantische klassesjabloondefinitie kunt u ook ten minste één parameter vereisen:
template <typename First, typename... Rest> class classname;
Hier volgt een basisvoorbeeld van de syntaxis van een variantische functiesjabloon :
template <typename... Arguments> returntype functionname(Arguments... args);
Het Arguments parameterpakket wordt vervolgens uitgebreid voor gebruik, zoals wordt weergegeven in de volgende sectie.
Andere vormen van syntaxis van een variantische functiesjabloon zijn mogelijk, waaronder, maar niet beperkt tot, deze voorbeelden:
template <typename... Arguments> returntype functionname(Arguments&... args);
template <typename... Arguments> returntype functionname(Arguments&&... args);
template <typename... Arguments> returntype functionname(Arguments*... args);
Aanduidingen zoals const zijn ook toegestaan:
template <typename... Arguments> returntype functionname(const Arguments&... args);
Net als bij variadic sjabloonklassedefinities kunt u functies maken waarvoor ten minste één parameter is vereist:
template <typename First, typename... Rest> returntype functionname(const First& first, const Rest&... args);
Variadic-sjablonen gebruiken de sizeof...() operator (niet gerelateerd aan de oudere sizeof() operator):
template<typename... Arguments>
void tfunc(const Arguments&... args)
{
constexpr auto numargs{ sizeof...(Arguments) };
X xobj[numargs]; // array of some previously defined type X
helper_func(xobj, args...);
}
Meer informatie over plaatsing van beletseltekens
Eerder beschreven in dit artikel het weglatingsteken dat parameterpakketten en uitbreidingen in deze vorm definieert: 'links van de parameternaam, wordt een parameterpakket ondertekend en rechts van de parameternaam wordt de parameterpakketten uitgebreid tot afzonderlijke namen'. Hoewel het technisch waar is, kan het verwarrend zijn bij het vertalen naar code. Overweeg het volgende:
In een sjabloonparameterlijst (
template <parameter-list>)typename...introduceert u een sjabloonparameterpakket.In een parameterdeclaratie-component (
func(parameter-list)) introduceert een beletselteken op het hoogste niveau een functieparameterpakket en is het plaatsen van het beletselteken belangrijk:// v1 is NOT a function parameter pack: template <typename... Types> void func1(std::vector<Types...> v1); // v2 IS a function parameter pack: template <typename... Types> void func2(std::vector<Types>... v2);Wanneer het beletselteken direct na een parameternaam wordt weergegeven, hebt u een uitbreiding van het parameterpakket.
Voorbeeld
Een goede manier om het sjabloonmechanisme voor variadic functies te illustreren, is door het te gebruiken in een herschrijf van een deel van de functionaliteit van printf:
#include <iostream>
using namespace std;
void print() {
cout << endl;
}
template <typename T> void print(const T& t) {
cout << t << endl;
}
template <typename First, typename... Rest> void print(const First& first, const Rest&... rest) {
cout << first << ", ";
print(rest...); // recursive call using pack expansion syntax
}
int main()
{
print(); // calls first overload, outputting only a newline
print(1); // calls second overload
// these call the third overload, the variadic template,
// which uses recursion as needed.
print(10, 20);
print(100, 200, 300);
print("first", 2, "third", 3.14159);
}
Uitvoer
1
10, 20
100, 200, 300
first, 2, third, 3.14159
Opmerking
De meeste implementaties die variadic functiesjablonen bevatten, maken gebruik van recursie van een bepaalde vorm, maar het is iets anders dan traditionele recursie. Traditionele recursie omvat een functie die zichzelf aanroept met behulp van dezelfde handtekening. (Het kan overbelast of gesjabloond zijn, maar elke keer wordt dezelfde handtekening gekozen.) Variadic recursie omvat het aanroepen van een variadic functiesjabloon met behulp van verschillende (bijna altijd afnemende) getallen van argumenten, en daardoor elke keer een andere handtekening uit te stempelen. Er is nog steeds een 'basiscase' vereist, maar de aard van de recursie is anders.