Variadiske maler eller kraften til tre prikker

 C Programming >> C C# Program >  >> C++
Variadiske maler eller kraften til tre prikker

En variadisk mal er en mal som kan ha et vilkårlig antall malparametere. Denne funksjonen kan virke magisk for deg hvis du ser den første gang. Så la meg avmystifisere variadiske maler.

Du lurer kanskje på at grafikken min som viser emnene jeg skriver om inkluderer instansiering av maler. Grunnen er enkel. Etter mitt siste innlegg om "Malinstansering", kom en av mine tyske lesere (pseudonym Urfahraner Auge) med en kommentar. Det er en viktig forskjell mellom implisitt og eksplisitt instansiering av en mal som jeg glemte å nevne. Han har rett. Den implisitte instansieringen av maler er lat, men den eksplisitte instansieringen av maler er ivrig.

Lazy versus ivrig mal-instansering

Malinstansering er lat. 03 Det betyr at hvis du ikke trenger en medlemsfunksjon i en klassemal, vil den ikke bli instansiert. Bare erklæringen om medlemsfunksjonen er tilgjengelig, men ikke definisjonen. Dette fungerer så langt at du kan bruke ugyldig kode i en medlemsfunksjon. Medlemfunksjonen må selvfølgelig ikke kalles.

// numberImplicitExplicit.cpp

#include <cmath>
#include <string>

template <typename T>
struct Number {
 int absValue() {
 return std::abs(val);
 }
 T val{};
};

// template class Number<std::string>; // (2)
// template int Number<std::string>::absValue(); // (3)

int main() {
 
 Number<std::string> numb;
 // numb.absValue(); // (1)
 
}

Hvis du kaller medlemsfunksjonen 18 (linje 1), får du det du kan forvente. En feilmelding under kompilering som i hovedsak sier at det ikke er noen overbelastning22 for 35 tilgjengelig. Her er de to første linjene fra den detaljerte feilmeldingen:

Jeg må forklare malinstansering mer presist: Den implisitte instansieringen av maler er lat, men den eksplisitte instansieringen av maler er ivrig.

Når du aktiverer linje (2) (48 ) og eksplisitt instansiert klassemalen 54 eller du aktiverer linje (3) (67 )) og eksplisitt instansiert medlemsfunksjonen 75 for 85 , får du en kompileringsfeil. Denne kompileringstidsfeilen tilsvarer kompilatorfeilen som påkaller medlemsfunksjonen absValue i linje (1) (90 ). Nok en gang, her er de to første linjene i feilmeldingene etter aktivering av linje (2) eller linje (3).

  • Linje (2) aktivert

  • Linje (3) aktivert

En personlig merknad:

Jeg er opptatt av å få kommentarer om innleggene mine. De hjelper meg å skrive om innholdet du ønsker å høre. Spesielt det tyske miljøet er veldig engasjert.

Nå, endelig til noe helt annet:variadiske maler.

Variadiske maler

En variadisk mal er en mal som kan ha et vilkårlig antall malparametere. Denne funksjonen kan virke magisk for deg hvis du ser den første gang.

template <typename ... Args>
void variadicTemplate(Args ... args) { 
 . . . . // four dots
}

Ellipsen (108 ) gjør 118 eller 126 en såkalt parameterpakke. Nettopp, 130 er en malparameterpakke og 149 er en funksjonsparameterpakke. To operasjoner er mulig med parameterpakker. De kan pakkes og pakkes ut. Hvis ellipsen er til venstre for 154 , vil parameterpakken bli pakket, hvis den er til høyre for 168 , den er pakket ut. På grunn av funksjonsmalargumentdeduksjonen kan kompilatoren utlede malargumentene.

Variadiske maler brukes ofte i standardmalbiblioteket og også i kjernespråket.

template <typename... Types> // (1)
class tuple; 

template <typename Callable, typename... Args > // (2)
explicit thread(Callable&& f, Args&&... args); 

template <typename Lockable1, typename Lockable2, typename... LockableN> // (3)
void lock(Lockable1& lock1, Lockable2& lock2, LockableN&... lockn);

sizeof...(ParameterPack); // (4)

Alle fire eksemplene fra C++11-standarden bruker variadiske maler. De tre første er en del av standard malbibliotek. La oss se hva jeg kan utlede av erklæringene.

  1. 173 godtar et vilkårlig antall forskjellige typer.
  2. 184 lar den påkalle en kallbar med et vilkårlig antall argumenter. Argumentasjonen kan ha forskjellige typer. En callable er noe du kan påkalle, for eksempel en funksjon, et funksjonsobjekt eller et lambda-uttrykk. Funksjonen 193 tar dets callable og dets argumenter ved universell referanse. Hvis du trenger flere detaljer:Jeg skrev allerede om malargumentfradrag og universelle referanser i mitt innlegg "Malargumenter".
  3. 205 lar den låse et vilkårlig antall låsbare typer i et atomtrinn. Å låse en låsbar type i et atomtrinn er trivielt. Følgelig, 216 krever minst to argumenter. 229 heter krav. Typer som støtter 233 må ha medlemsfunksjonene243 , 255 og 267 .
  4. 276 - operator returnerer antall elementer i 282 .

295 -operator ser ut til å være spesiell fordi ParameterPack brukes i kjernespråket. La meg skrive noen ord om det.

308 .-Operator

Takket være 312 ...-operator kan brukes til direkte å bestemme hvor mange elementer en parameterpakke inneholder. Elementene blir ikke evaluert.

// printSize.cpp

#include <iostream>

using namespace std::literals;

template <typename ... Args>
void printSize(Args&& ... args){
 std::cout << sizeof...(Args) << ' '; // (1)
 std::cout << sizeof...(args) << '\n'; // (2)
}

int main() {

 std::cout << '\n';

 printSize(); // (3)
 printSize("C string", "C++ string"s, 2011, true); // (4)

 std::cout << '\n';

}

326 ..-operator lar den bestemme størrelsen på malparameterpakken (1) og funksjonsparameterpakken (2) på kompileringstidspunktet. Jeg bruker den på en tom parameterpakke (3), og en parameterpakke som inneholder fire elementer. Det første elementet er en C-streng og det andre en C++-streng. For å bruke bokstaven C++-streng, må jeg inkludere navneområdet335 (5). C++14 støtter C++ strengliteraler.

Hva er det neste?

I mitt neste innlegg dykker jeg dypere inn i variadiske maler og introduserer det funksjonelle mønsteret for å evaluere en variadisk mal. I tillegg presenterer jeg den perfekte fabrikkfunksjonen og hopper fra C++11 til C++17:fold uttrykk i C++17.