Variabel skabelon

Den aktuelle version af siden er endnu ikke blevet gennemgået af erfarne bidragydere og kan afvige væsentligt fra den version , der blev gennemgået den 2. april 2019; checks kræver 6 redigeringer .

En variabel skabelon eller en skabelon med et variabelt antal argumenter ( eng.  Variadic Template ) i programmering  er en skabelon med et hidtil ukendt antal argumenter, der danner en eller flere såkaldte parameterpakker .

Den variadiske skabelon giver dig mulighed for at bruge typeparameterisering, hvor du vil operere på et vilkårligt antal argumenter, som hver har en vilkårlig type [1] . Det kan være meget praktisk i situationer, hvor skabelonadfærdsscenariet kan generaliseres til en ukendt mængde modtagne data [2] .

Variable skabeloner understøttes i C++ (siden C++11 -standarden ) og D .

C++

Variabel skabelon i C++ (også kendt som parameter pack ) blev udviklet af Douglas Gregor og Jaakko Järvi [3] [4] og blev senere standardiseret i C++11. Før C++11 kunne skabeloner (af klasser og funktioner) kun tage et fast antal argumenter, som skulle defineres, da skabelonen først blev erklæret.

Variabel skabelonsyntaks:

skabelon < typenavn ... Værdier > klasse tuple ;

Tuple  -klasseskabelonen ovenfor kan tage et hvilket som helst antal inputparametre. For eksempel oprettes en instans af ovenstående skabelonklasse med tre argumenter:

tuple < int , std :: vektor < int > , std :: map << std :: string > , std :: vektor < int >>> some_instance_name ;

Antallet af argumenter kan være nul, så det tuple<> some_instance_name;vil også fungere. Hvis du ikke vil tillade, at variantskabelonobjekter oprettes med nul argumenter, kan du bruge følgende erklæring:

skabelon < typenavn Først typenavn ... Rest > klasse tuple ; _

Variable skabeloner kan også anvendes på funktioner.

skabelon < typenavn ... Params > void printf ( const std :: string & str_format , Params ... parametre );

Ellipseoperatoren (...) spiller to roller. Når det vises til venstre for et funktionsparameternavn, erklærer det et sæt parametre. Når ellipseoperatoren er til højre for et skabelon- eller funktionskaldsargument, pakker den parametrene ud i separate argumenter, ligesom args...i brødteksten printfnedenfor.

void printf ( const char * s ) { mens ( * s ) { if ( * s == '%' ) { if ( * ( s + 1 ) == '%' ) { ++ s ; } andet { throw std :: runtime_error ( "dårligt strengformat: manglende argumenter" ); } } std :: cout << * s ++ ; } } skabelon < typenavn T , typenavn ... Args > void printf ( const char * s , T - værdi , Args ... args ) { mens ( * s ) { if ( * s == '%' ) { if ( * ( s + 1 ) == '%' ) { ++ s ; } andet { std :: cout << værdi ; s += 2 ; // virker kun for to tegnformater (f.eks. %d, %f ). Virker ikke med %5.4f printf ( s , args ...); // kaldet forekommer, selv når *s == 0 for at detektere overflødige argumenter returnere ; } } std :: cout << * s ++ ; } }

Dette er et rekursivt mønster. Bemærk, at denne skabelonvariantversion af printf kalder sig selv eller (hvis args... er tom) standardvarianten.

Der er ingen enkel mekanisme til at opregne værdierne af variable skabeloner. Der er flere måder at konvertere en liste med argumenter til et enkelt argument. Dette implementeres normalt ved hjælp af funktionsoverbelastning, eller hvis funktionen kun kan tage et argument ad gangen, ved hjælp af en simpel udvidelsesmarkør:

skabelon < typename ... Args > inline void pass ( Args && ...) {}

denne skabelon kan bruges sådan her:

skabelon < typenavn ... Args > inline void expand ( Args && ... args ) { pass ( some_function ( args )... ); } expand ( 42 , "svar" , sandt );

og det vil blive konverteret til noget som:

pass ( some_funktion ( arg1 ) , nogle_funktion ( arg2 ) , nogle_funktion ( arg3 ) osv ... ) ; _

Brugen af ​​"pass"-funktionen er nødvendig, fordi argumentudpakning sker ved at adskille funktionsargumenter adskilt af kommaer, som ikke svarer til kommaoperatoren. Derfor some_function(args)...; vil aldrig virke. Desuden vil løsningen ovenfor kun fungere, når returtypen for some_function ikke er ugyldig . Kald til some_function vil også blive udført i en vilkårlig rækkefølge, fordi rækkefølgen, hvori funktionsargumenter evalueres, er udefineret. For at undgå vilkårlig rækkefølge kan en initialiseringsliste i parentes bruges til at sikre, at sekvensen fra venstre mod højre opretholdes.

struct pass { skabelon < typenavn ... T > bestået ( T ...) {} }; pass {( some_function ( args ), 1 )...};

I stedet for at kalde en funktion, kan du oprette et lambda-udtryk og udføre det på plads.

pass{([&]{ std::cout << args << std::endl; }(), 1)...};

I dette særlige eksempel er lambdafunktionen dog ikke påkrævet. Du kan bruge regulære udtryk:

pass{(std::cout << args << std::endl, 1)...};

En anden måde er at bruge funktionsoverbelastning. Dette er en mere alsidig måde, men kræver lidt flere linjer med kode og indsats. Den ene funktion tager et argument af en eller anden type og et sæt argumenter, mens den anden (terminal) ikke tager noget. Hvis begge funktioner har den samme liste over startparametre, vil opkaldet være tvetydigt. For eksempel:

void func () {} // endelig version skabelon < typenavn Arg1 , typenavn ... Args > void func ( const Arg1 & arg1 , const Args & ... args ) { proces ( arg1 ); func ( args ...); // bemærk: arg1 vises ikke her! }

Variable skabeloner kan også bruges i undtagelser, basisklasselister eller constructorinitialiseringslister. For eksempel kan en klasse arve følgende:

skabelon < typenavn ... BaseClasses > klasse Klassenavn : public BaseClasses ... { offentligt : Klassenavn ( BaseClasses && ... base_classes ) : BaseClasses ( base_classes )... {} };

Unboxing-operatøren vil erstatte basisklasserne med den afledte klasse ClassName; således vil denne klasse arve alle klasser, der er overført til den. Derudover skal konstruktøren acceptere en reference til hver basisklasse.

Hvad angår variadiske funktionsskabeloner , kan parametre videresendes. Kombineret med det universelle link (se ovenfor) giver dette mulighed for fremragende videresendelse:

skabelon < typenavn TypeToConstruct > struct SharedPtrAllocator { skabelon < typenavn ... Args > std :: shared_ptr < TypeToConstruct > construct_with_shared_ptr ( Args && ... params ) { return std :: shared_ptr < TypeToConstruct > ( ny TypeToConstruct ( std :: forward < Args > ( params )...)); } };

Denne kode pakker argumentlisten ud i en TypeToConstruct-konstruktør. Syntaksen std::forward<Args>(params)videregiver argumenterne såvel som deres typer, selv med rvalue-karakteristikken, til konstruktøren. Denne fabriksfunktion tildeler automatisk allokeret hukommelse for at std::shared_ptrforhindre hukommelseslækager.

Derudover kan antallet af parametre i en skabelon defineres som følger:

skabelon < typenavn ... Args > struct SomeStruct { static const int size = sizeof ...( Args ); };

Udtrykket SomeStruct<Type1, Type2>::sizereturnerer 2 og udtrykket SomeStruct<>::sizereturnerer 0.

Eksempel på summeringsfunktion: dobbeltsum ( dobbelt x ) _ { returnere x ; } skabelon < klasse ... Args > dobbeltsum ( dobbelt x , Args ... args ) _ { returner x + sum ( args ...); }

Se også

Noter

  1. Vandewoerd, Josattis, Gregor, 2018 , Variable Patterns, s. 89.
  2. Vandewoerd, Josattis, Gregor, 2018 , Variable Patterns, s. 243.
  3. Douglas Gregor og Jaakko Järvi.
  4. Douglas Gregor, Jaakko Järvi og Gary Powell.

Kilder

  • D. Vandevoerd, N. Josattis, D. Gregor. C++ skabeloner. Udviklerens reference = C++ skabeloner. Den komplette guide. - 2. - Sankt Petersborg.  : "Alfa-bog", 2018. - 848 s. - ISBN 978-5-9500296-8-4 .

Links