Muuttuva malli

Kokeneet kirjoittajat eivät ole vielä tarkistaneet sivun nykyistä versiota, ja se voi poiketa merkittävästi 2. huhtikuuta 2019 tarkistetusta versiosta . tarkastukset vaativat 6 muokkausta .

Ohjelmoinnissa muuttujamalli tai vaihtuva määrä argumentteja sisältävä mallipohja ( eng.  Variadic Template )  on mallipohja , jossa on aiemmin tuntematon määrä argumentteja, jotka muodostavat yhden tai useamman ns. parametripaketin .

Variadic-mallin avulla voit käyttää tyyppiparametreja, kun haluat käyttää mielivaltaista määrää argumentteja, joista jokaisella on mielivaltainen tyyppi [1] . Se voi olla erittäin kätevä tilanteissa, joissa mallin käyttäytymisskenaario voidaan yleistää tuntemattomaan määrään vastaanotettua dataa [2] .

Muuttujamalleja tuetaan C++ :ssa ( C++11 -standardista lähtien ) ja D :ssä .

C++

Muuttujamalli C++:ssa (tunnetaan myös nimellä parametripaketti ) kehitettiin Douglas Gregorin ja Jaakko Järvin [3] [4] toimesta ja standardisoitiin myöhemmin C++11:ssä. Ennen C++11:tä mallit (luokkien ja funktioiden) saattoivat ottaa vain kiinteän määrän argumentteja, jotka piti määritellä, kun mallia ilmoitettiin ensimmäisen kerran.

Muuttujan mallin syntaksi:

malli < tyypin nimi ... Arvot > luokka monikko ;

Yllä oleva tuple  - luokkamalli voi ottaa minkä tahansa määrän syöttöparametreja. Esimerkiksi yllä olevan malliluokan esiintymä luodaan kolmella argumentilla:

monikko < int , std :: vektori < int > , std :: kartta << std :: merkkijono > , std :: vektori < int >>> jonkin_esiintymän_nimi ;

Argumenttien määrä voi olla nolla, joten tuple<> some_instance_name;se myös toimii. Jos et halua sallia mallimalliobjektien luomisen nollaargumenteilla, voit käyttää seuraavaa ilmoitusta:

template < typename First , typename ... Rest > class tuple ;

Muuttujamalleja voidaan soveltaa myös funktioihin.

template < typename ... Parametrit > void printf ( const std :: string & str_format , Params ... parametrit );

Ellipsin operaattorilla (...) on kaksi roolia. Kun se näkyy funktion parametrin nimen vasemmalla puolella, se ilmoittaa joukon parametreja. Kun ellipsioperaattori on mallin tai funktion kutsuargumentin oikealla puolella, se purkaa parametrit erillisiksi argumenteiksi, aivan kuten args...alla olevassa rungossa printf.

void printf ( const char * s ) { while ( * s ) { if ( * s == '%' ) { if ( * ( s + 1 ) == '%' ) { ++ s ; } else { throw std :: runtime_error ( "huono merkkijonomuoto: puuttuvat argumentit" ); } } std :: cout << * s ++ ; } } malli < typename T , typename ... Args > void printf ( const char * s , T - arvo , Args ... args ) { while ( * s ) { if ( * s == '%' ) { if ( * ( s + 1 ) == '%' ) { ++ s ; } else { std :: cout << arvo ; s += 2 ; // toimii vain kahdelle merkkimuodon määrittimelle (esim. %d, %f ). Ei toimi kanssa %5.4f printf ( s , args ...); // kutsu esiintyy, vaikka *s == 0 havaitsemaan ylimääräisiä argumentteja paluu ; } } std :: cout << * s ++ ; } }

Tämä on rekursiivinen kuvio. Huomaa, että tämä printf :n malliversion versio kutsuu itseään tai (jos args... on tyhjä) oletusmuunnelmaa.

Ei ole olemassa yksinkertaista mekanismia muuttujamallien arvojen luettelemiseksi. On olemassa useita tapoja muuntaa argumenttiluettelo yhdeksi argumentiksi. Tämä toteutetaan yleensä käyttämällä funktion ylikuormitusta tai jos funktio voi ottaa vain yhden argumentin kerrallaan, käyttämällä yksinkertaista laajennusmerkkiä:

malli < typename ... Args > tekstin sisäinen void pass ( Args && ...) {}

tätä mallia voidaan käyttää näin:

template < typename ... Args > inline void expand ( Args && ... args ) { pass ( some_function ( args )... ); } laajentaa ( 42 , "vastaus" , tosi );

ja se muunnetaan sellaiseksi:

pass ( joku_funktio ( arg1 ) , jokin_funktio ( arg2 ) , jokin_funktio ( arg3 ) jne ... ) ; _

"Pass"-funktion käyttö on välttämätöntä, koska argumentin purkaminen tapahtuu erottamalla funktion argumentit toisistaan ​​pilkuilla, jotka eivät vastaa pilkkuoperaattoria. Siksi some_function(args)...; ei koskaan toimi. Lisäksi yllä oleva ratkaisu toimii vain, kun jonkin_funktion palautustyyppi ei ole void . Myös jonkin_funktion kutsut suoritetaan mielivaltaisessa järjestyksessä, koska järjestystä, jossa funktion argumentit arvioidaan, ei ole määritelty. Mielivaltaisen järjestyksen välttämiseksi voidaan käyttää suluissa olevaa alustusluetteloa varmistamaan vasemmalta oikealle järjestyksen säilyminen.

struct pass { malli < typename ... T > pass ( T ...) {} }; pass {( some_function ( args ), 1 )...};

Funktion kutsumisen sijaan voit luoda lambda-lausekkeen ja suorittaa sen paikallaan.

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

Tässä nimenomaisessa esimerkissä lambda-toimintoa ei kuitenkaan vaadita. Voit käyttää säännöllisiä lausekkeita:

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

Toinen tapa on käyttää toimintojen ylikuormitusta. Tämä on monipuolisempi tapa, mutta vaatii hieman enemmän koodirivejä ja vaivaa. Yksi funktio ottaa yhden jonkinlaisen argumentin ja joukon argumentteja, kun taas toinen (pääte) ei ota mitään. Jos molemmilla funktioilla on sama alkuparametriluettelo, kutsu on epäselvä. Esimerkiksi:

void func () {} // lopullinen versio malli < typename Arg1 , typename ... Args > void func ( const Arg1 & arg1 , const Args & ... args ) { prosessi ( arg1 ); func ( args ...); // huomautus: arg1 ei näy täällä! }

Muuttujamalleja voidaan käyttää myös poikkeuksissa, perusluokkaluetteloissa tai rakentajien alustusluetteloissa. Luokka voi esimerkiksi periä seuraavat asiat:

template < typename ... BaseClasses > class ClassName : public BaseClasses ... { julkinen : LuokanNimi ( BaseClasses && ... base_classes ) : Perusluokat ( perusluokat )... {} };

Unboxing-operaattori korvaa johdetun luokan perusluokat ClassName; joten tämä luokka perii kaikki sille välitetyt luokat. Lisäksi rakentajan on hyväksyttävä viittaus jokaiseen perusluokkaan.

Mitä tulee variadic -funktiomalleihin , parametrit voidaan lähettää eteenpäin. Yhdessä yleislinkin (katso yllä) kanssa tämä mahdollistaa erinomaisen edelleenlähetyksen:

template < typename TypeToConstruct > struct SharedPtrAllocator { template < typename ... Args > std :: share_ptr < TypeToConstruct > construct_with_shared_ptr ( Args && ... params ) { return std :: shared_ptr < TypeToConstruct > ( new TypeToConstruct ( std :: forward < Args > ( params )...)); } };

Tämä koodi purkaa argumenttiluettelon TypeToConstruct-konstruktoriin. Syntaksi std::forward<Args>(params)välittää argumentit ja niiden tyypit, myös rvalue-ominaisuudella, rakentajalle. Tämä tehdastoiminto määrittää automaattisesti varatun muistin std::shared_ptrmuistivuotojen estämiseksi.

Lisäksi mallin parametrien määrä voidaan määrittää seuraavasti:

template < typename ... Args > struct SomeStruct { static const int koko = sizeof ...( Args ); };

Lauseke SomeStruct<Type1, Type2>::sizepalauttaa arvon 2 ja lauseke SomeStruct<>::size0.

Esimerkki summausfunktiosta: tuplasumma ( tupla x ) _ { paluu x ; } malli < luokka ... Args > tuplasumma ( tupla x , args ... args ) _ { paluu x + summa ( args ...); }

Katso myös

Muistiinpanot

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

Lähteet

  • D. Vandevoerd, N. Josattis, D. Gregor. C++-malleja. Kehittäjän viite = C++ Templates. Täydellinen opas. - 2. - Pietari.  : "Alfa-kirja", 2018. - 848 s. - ISBN 978-5-9500296-8-4 .

Linkit