C ++ 17 vs. C ++ 14 - hvis-constexpr

Vi er glade for at se, at hvis-constexpr kom til C ++ 17. Du kan prøve det selv ved hjælp af den nuværende stamme af klang.

I dette blog-indlæg besøger vi nogle C ++ 14-koder og prøver at bruge den nye funktion.

Hvis du ikke har if-constexpr til din rådighed, er du ofte nødt til at ty til detaljerede metaprogrammeringsteknikker ved at bruge skabelonmønstermatchning, overbelastningsopløsning og SFINAE.

Eksempel 1 - at få n-arg

Mange skabelonmetaprogrammer fungerer på variadistypelister. I C ++ 14 implementeres ofte at få nth-typen af ​​en argumenteliste på følgende måde:

skabelon 
struct Arg {
 skabelon 
 constexpr auto operator () (X x, Xs ... xs) {
   returner Arg  {} (xs ...);
 }
};
skabelon <>
struct Arg <0> {
 skabelon 
 constexpr auto operator () (X x, Xs ...) {
   returnere x;
 }
};
skabelon 
constexpr auto arg = Arg  {};
// arg <2> (0,1,2,3,4,5) == 2;

C ++ 17 gør dette lidt mere intuitivt:

skabelon 
struktur Få {
 skabelon 
 constexpr auto operator () (X x, Xs ... xs) {
   hvis constexpr (n> størrelse af… (xs)) {
     Vend tilbage;
   } andet hvis constexpr (n> 0) {
     retur Hent  {} (xs ...);
   } andet {
     returnere x;
   }
 }
};

Eksempel 2 - API - shimming

Nogle gange vil du støtte et alternativt API. C ++ 14 giver en nem måde at kontrollere, om et objekt kan bruges på en bestemt måde:

skabelon 
constexpr auto understøtterAPI (T x) -> decltype (x.Method1 (), x.Method2 (), true_type {}) {
 Vend tilbage {};
}
constexpr auto understøtterAPI (…) -> false_type {
 Vend tilbage {};
}

Implementering af tilpasset opførsel i C ++ 14 kan gøres sådan:

skabelon 
automatisk beregning (T x) -> dekltype (enable_if_t  {}) {
 retur x.Metode ();
}
skabelon 
automatisk beregning (T x) -> dekltype (enable_if_t  {}) {
 retur 0;
}

C ++ 17:

skabelon 
int beregne (T x) {
 hvis constexpr (understøtterAPI (T {})) {
   // bliver kun samlet, hvis betingelsen er sand
   retur x.Metode ();
 } andet {
   retur 0;
 }
}

Dette er meget praktisk, da kode, der hører semantisk sammen, ikke er spredt over flere funktioner. Desuden kan du endda definere lambdas, der indeholder if-constexpr.

Eksempel 3 - Valg af kompileringstid algoritme

Ofte skal du finde den bedste algoritme baseret på et sæt regler og egenskaber af en type. Der er mange løsninger. For eksempel bruger STL TypeTags til at vælge den rigtige algoritme for nogle givne iteratorer.

struct FooTag {};
struct BarTag {};
auto foldFF (…) {}
auto foldFB (…) {}
auto foldBF (…) {}
auto foldBB (…) {}
struktur A {
 / *… * /
 ved hjælp af tag = FooTag;
};
struktur B {
 / *… * /
 ved hjælp af tag = BarTag;
};
skabelon 
auto fold (L l, R r, FooTag, BarTag) {foldFB (l, r); }
/ * flere forsendelsesfunktioner * /
skabelon 
auto fold (L l, R r) {
 returfoldning (l, r,
 typebetegnelse L :: tag {},
 typebetegnelse R :: tag {});
}

Når du først har mere komplekse regler, har du muligvis brug for en mere kraftfuld løsning - SFINAE:

C ++ 14:

struct BazTag: FooTag, BarTag {};
skabelon  :: værdi &&
 is_base_of  :: værdi
> fold (L l, R r) {
 returnerer foldFB (l, r);
}

Med C ++ 17 kan du beskrive disse regler med mindre kedelplade og på en klarere måde:

skabelon 
auto fold (L l, R r) {
 ved hjælp af lTag = typebetegnelse L :: tag;
 ved hjælp af rTag = typebetegnelse R :: tag;
hvis constexpr (er_base_of  :: værdi) {
 hvis constexpr (is_same  :: værdi) {
   returnerer foldFB (l, r);
 } andet {
   returnerer foldBB (l, r);
 } andet {
   return foldFF ();
 }
}

Dette er meget praktisk, da det er mere intuitivt at arbejde med ifs end at bruge forskellige sprogfunktioner.

Refactoring metafunktioner bliver så enkel som almindelig kode. Med if-constexpr er bekymring over tvetydig overbelastning og andre uventede komplikationer fortiden.

Vi opgraderer vores compiler, så snart Clang 3.9 er stabil.