Programmēšana

Ievads metaprogrammēšanā C ++

Iepriekšējais 1 2 3 3. lpp 3. lapa no 3
  • Stāvokļa mainīgie: veidnes parametri
  • Cilpas konstrukcijas: caur rekursiju
  • Izpildes ceļu vēlēšanas: izmantojot nosacītas izteiksmes vai specializācijas
  • Aritmētika ar veselo skaitli

Ja rekursīvo instanču skaitam un atļauto stāvokļa mainīgo skaitam nav ierobežojumu, tas ir pietiekami, lai aprēķinātu visu, kas ir aprēķināms. Tomēr, iespējams, nav ērti to izdarīt, izmantojot veidnes. Turklāt, tā kā veidņu tūlītējai sagatavošanai ir nepieciešami ievērojami kompilatora resursi, plaša rekursīvā instantācija ātri palēnina kompilatora darbību vai pat iztērē pieejamos resursus. C ++ standarts iesaka, bet neuzliek par pienākumu atļaut vismaz 1 024 rekursīvo instanču līmeņus, kas ir pietiekami lielākajai daļai (bet noteikti ne visu) veidņu metaprogrammēšanas uzdevumu veikšanai.

Tādējādi praksē veidņu metaprogrammas jāizmanto taupīgi. Tomēr ir dažas situācijas, kad tās ir neaizstājamas kā ērtu veidņu ieviešanas rīks. Jo īpaši tos dažreiz var paslēpt tradicionālāku veidņu iekšpusē, lai izspiestu lielāku veiktspēju no kritisko algoritmu ieviešanas.

Rekursīvā instantiation salīdzinājumā ar rekursīvajiem veidņu argumentiem

Apsveriet šādu rekursīvo veidni:

veidnes struktūra Doublify {}; template struct Problēma {izmantojot LongType = Doublify; }; template struct Problēma {izmantojot LongType = double; }; Problēmas :: LongType ouch;

Pielietojums Problēmas :: LongType ne tikai izraisa rekursīvo instantiation Nepatikšanas, Nepatikšanas, …, Nepatikšanas, bet tas arī acumirklī Apšaubīt vairāk nekā arvien sarežģītāki veidi. Tabula parāda, cik ātri tā aug.

Gada izaugsme Problēmas :: LongType

 
Ierakstiet aizstājvārduPamata tips
Problēmas :: LongTypedubultā
Problēmas :: LongTypeApšaubīt
Problēmas :: LongTypeApšaubīt<>

Divkāršojiet>

Problēmas :: LongTypeApšaubīt<>

Divkāršot>,

   <>

Divkāršojiet >>

Kā redzams tabulā, izteiksmes veida apraksta sarežģītība Problēmas :: LongType pieaug eksponenciāli ar N. Šādā situācijā C ++ kompilators tiek uzsvērts vēl vairāk nekā rekurzīvie eksemplāri, kas neietver rekursīvus veidņu argumentus. Viena no problēmām šeit ir tā, ka kompilators saglabā tipa saīsinātā nosaukuma attēlojumu. Šis sajauktais nosaukums kaut kādā veidā kodē precīzu veidņu specializāciju, un agrīnā C ++ ieviešanā tika izmantots kodējums, kas ir aptuveni proporcionāls veidnes-id garumam. Šie sastādītāji pēc tam izmantoja krietni vairāk nekā 10 000 rakstzīmes Problēmas :: LongType.

Jaunākās C ++ implementācijās tiek ņemts vērā fakts, ka ligzdotie veidņu ID ir diezgan izplatīti mūsdienu C ++ programmās, un izmanto gudras saspiešanas metodes, lai ievērojami samazinātu nosaukumu kodēšanas pieaugumu (piemēram, daži simti rakstzīmju Problēmas :: LongType). Šie jaunākie kompilatori arī izvairās no sajaukta nosaukuma ģenerēšanas, ja tāds faktiski nav vajadzīgs, jo veidnes gadījumam faktiski netiek ģenerēts zema līmeņa kods. Tomēr, ja visas pārējās lietas ir vienādas, visticamāk, ir vēlams organizēt rekursīvo instantiāciju tā, lai šablonu argumenti nebūtu jāievieto arī rekursīvi.

Uzskaitīšanas vērtības pret statiskajām konstantēm

C ++ sākuma dienās uzskaites vērtības bija vienīgais mehānisms, lai izveidotu “patiesās konstantes” (sauktas nemainīgas izteiksmes) kā nosauktie biedri klases deklarācijās. Ar tiem jūs, piemēram, varētu definēt a Pow3 metaprogrammu, lai aprēķinātu 3 jaudas šādi:

meta / pow3enum.hpp // primārā veidne, lai aprēķinātu 3. N veidnes struktūru Pow3 {enum {value = 3 * Pow3 :: value}; }; // pilna specializācija, lai izbeigtu rekursijas veidni struct Pow3 {enum {value = 1}; };

Standartizējot C ++ 98, tika ieviests klases statisko nemainīgo inicializatoru jēdziens, lai Pow3 metaprogramma varētu izskatīties šādi:

meta / pow3const.hpp // primārā veidne, lai aprēķinātu 3. N veidnes struktūru Pow3 {static int const value = 3 * Pow3 :: value; }; // pilnīga specializācija rekursijas veidnes izbeigšanai struct Pow3 {static int const value = 1; };

Tomēr šai versijai ir trūkums: statiskie konstanti locekļi ir vērtības. Tātad, ja jums ir tāda deklarācija kā

void foo (int const &);

un jūs tam nododat metaprogrammas rezultātu:

foo (Pow3 :: vērtība);

kompilatoram jānokārto adrese gada Pow3 :: vērtība, un tas liek kompilatoram precizēt un piešķirt statiskā locekļa definīciju. Rezultātā aprēķins vairs nav ierobežots ar tīru “sastādīšanas laika” efektu.

Uzskaitīšanas vērtības nav vērtības (tas ir, tām nav adreses). Tātad, nododot tos atsaucei, netiek izmantota statiskā atmiņa. Tas ir gandrīz tieši tā, it kā jūs aprēķināto vērtību nodotu kā burtisku.

C ++ 11 tomēr tika ieviests konstexpr statisko datu dalībnieki, un tie neaprobežojas tikai ar integrālajiem tipiem. Tie neatrisina iepriekš izvirzīto adreses problēmu, taču, neraugoties uz šo trūkumu, tie tagad ir izplatīts veids, kā iegūt metaprogrammu rezultātus. Viņiem ir priekšrocība, ka tiem ir pareizs tips (atšķirībā no mākslīgā enum tipa), un šo tipu var secināt, kad statiskais loceklis tiek deklarēts ar automātiskā tipa specifikatoru. C ++ 17 pievienoja tiešos statiskos datu dalībniekus, kuri atrisina iepriekš izvirzīto adreses problēmu un kurus var izmantot kopā konstexpr.

Metaprogrammēšanas vēsture

Agrākais dokumentētais metaprogrammas piemērs bija Ervins Unruhs, kurš toreiz pārstāvēja Siemens C ++ standartizācijas komitejā. Viņš atzīmēja veidnes instantācijas procesa skaitļošanas pilnīgumu un parādīja savu nostāju, izstrādājot pirmo metaprogrammu. Viņš izmantoja Metaware kompilatoru un pierunāja to izdot kļūdas ziņojumus, kuros būtu secīgi galvenie skaitļi. Šis ir kods, kas tika izplatīts C ++ komitejas sanāksmē 1994. gadā (modificēts tā, ka tagad tas tiek apkopots uz standarta atbilstošiem kompilatoriem):

meta / unruh.cpp // galvenā skaitļa aprēķins // (modificēts ar oriģināla atļauju no 1994. gada ar Ervinu Unruhu) veidne struct is_prime {enum ((p% i) && is_prime2? p: 0), i-1> :: pri); }; veidnes struktūra is_prime {enum {pri = 1}; }; veidnes struktūra is_prime {enum {pri = 1}; }; veidne strukturēts D {D (spēkā neesošs *); }; veidne struct CondNull {statiskā int const vērtība = i; }; veidnes struktūras CondNull {static void * vērtība; }; void * CondNull :: vērtība = 0; veidne struct Prime_print {

// primārā cilpa veidne, lai drukātu primārus skaitļus Prime_print a; enum {pri = is_prime :: pri}; void f () {D d = CondNull :: vērtība;

// 1 ir kļūda, 0 ir labi a.f (); }}; veidne struct Prime_print {

// pilna specializācija, lai izbeigtu cikla enum {pri = 0}; tukšums f () {D d = 0; }; }; #ifndef LAST #define LAST 18 #endif int main () {Prime_print a; a.f (); }

Ja jūs kompilējat šo programmu, kompilators drukās kļūdas ziņojumus, kad, Prime_print :: f (), d inicializēšana neizdodas. Tas notiek, ja sākotnējā vērtība ir 1, jo void * ir tikai konstruktors, un tikai 0 ir derīga konvertācija uz spēkā neesošs *. Piemēram, vienā kompilatorā mēs (starp vairākiem citiem ziņojumiem) saņemam šādas kļūdas:

unruh.cpp: 39: 14: kļūda: nav dzīvotspējīgas konvertēšanas no 'const int' uz 'D' unruh.cpp: 39: 14: kļūda: nav dzīvotspējīgas konvertēšanas no 'const int' uz 'D' unruh.cpp: 39: 14: kļūda: nav dzīvotspējīgas konvertēšanas no 'const int' uz 'D' unruh.cpp: 39: 14: kļūda: nav dzīvotspējīgas konvertēšanas no 'const int' uz 'D' unruh.cpp: 39: 14: kļūda: nav dzīvotspējīga konvertēšana no 'const int' uz 'D' unruh.cpp: 39: 14: kļūda: nav dzīvotspējīgas konvertēšanas no 'const int' uz 'D' unruh.cpp: 39: 14: kļūda: nav dzīvotspējīgas konvertēšanas no 'const int' uz “D”

Piezīme. Tā kā kļūdu apstrāde kompilatoros atšķiras, daži kompilatori var apstāties pēc pirmā kļūdas ziņojuma izdrukāšanas.

C ++ veidņu metaprogrammēšanas kā nopietna programmēšanas rīka jēdzienu pirmo reizi popularizēja (un nedaudz formalizēja) Tods Veldhuizens savā rakstā “C ++ veidņu metaprogrammu izmantošana”. Veldhuizena darbs pie Blitz ++ (ciparu masīvu bibliotēka C ++) arī ieviesa daudzus precizējumus un paplašinājumus metaprogrammēšanai (un izteiksmes veidņu metodēm).

Gan šīs grāmatas pirmais izdevums, gan Andreja Aleksandresku Mūsdienu C ++ dizains veicināja C ++ bibliotēku eksploziju, izmantojot metaprogrammēšanu uz veidnēm, katalogizējot dažas no galvenajām metodēm, kas joprojām tiek izmantotas. Boost projekts bija nozīmīgs, lai ieviestu kārtību šajā sprādzienā. Sākumā tas ieviesa MPL (metaprogrammēšanas bibliotēku), kurā tika definēta konsekventa sistēma tipa metaprogrammēšana padarījis populāru arī caur Deivida Abrahama un Alekseja Gurtovoja grāmatu C ++ veidņu metaprogrammēšana.

Louis Dionne ir guvis papildu svarīgus sasniegumus, padarot metaprogrammēšanu sintaktiski pieejamāku, jo īpaši izmantojot savu Boost.Hana bibliotēku. Dionne kopā ar Endrjū Satonu, Herbu Sutteru, Deividu Vandevoordu un citiem tagad ir standartizācijas komitejas vadītāji, lai sniegtu pirmās klases metaprogrammēšanas atbalstu valodā. Svarīgs pamats šim darbam ir izpētīt, kādām programmas īpašībām vajadzētu būt pieejamām, pārdomājot; Galvenie ieguldītāji šajā jomā ir Matūšs Čohliks, Aksels Naumans un Deivids Sankels.

Džons Dž. Bartons un Lī R. Nekmens ilustrēja, kā izsekot dimensiju vienībām, veicot aprēķinus. SIunits bibliotēka bija plašāka bibliotēka darbam ar fiziskām vienībām, kuru izstrādāja Valters Brauns. The std :: chrono komponents standarta bibliotēkā nodarbojas tikai ar laiku un datumiem, un to palīdzēja Howard Hinnant.

$config[zx-auto] not found$config[zx-overlay] not found