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

Estem entusiasmats de veure que if-constexpr ho va fer a C ++ 17. Podeu provar-ho vosaltres mateixos utilitzant el tronc actual de clang.

En aquesta publicació al bloc revisem algun codi C ++ 14 i intentem utilitzar la nova funció.

No teniu if-constexpr a la vostra disposició, sovint cal recórrer a elaborar tècniques de meta-programació, utilitzant la concordança de patrons, resolució de sobrecàrregues i SFINAE.

Exemple 1: obtenció del nth-arg

Molts meta-programes de plantilles operen en llistes de tipus variadic. A C ++ 14, obtenir el tipus enèsim d'una llista de arguments sovint s'implementa de la manera següent:

plantilla 
struct Arg {
 plantilla 
 operador automàtic constexpr () (X x, Xs ... xs) {
   retorna Arg  {} (xs ...);
 }
};
plantilla <>
struct Arg <0> {
 plantilla 
 operador automàtic constexpr () (X x, X ...) {
   retornar x;
 }
};
plantilla 
constexpr auto arg = Arg  {};
// arg <2> (0,1,2,3,4,5) == 2;

C ++ 17 ho fa una mica més intuïtiu:

plantilla 
struct Obtenir {
 plantilla 
 operador automàtic constexpr () (X x, Xs ... xs) {
   si constexpr (n> sizeof ... (xs)) {
     retorn;
   } més si constexpr (n> 0) {
     retornar Obteniu  {} (xs ...);
   } més {
     retornar x;
   }
 }
};

Exemple 2: API - aprimament

De vegades es vol donar suport a una API alternativa. C ++ 14 proporciona una manera senzilla de comprovar si un objecte es pot utilitzar d'una determinada manera:

plantilla 
constexpr auto supportsAPI (T x) -> decltype (x.Method1 (), x.Method2 (), true_type {}) {
 tornar {};
}
constexpr auto supportsAPI (...) -> false_type {
 tornar {};
}

Implementant un comportament personalitzat en C ++ 14 es pot fer així:

plantilla 
auto calcula (T x) -> descltype (enable_if_t  {}) {
 retornar x.Method ();
}
plantilla 
auto calcula (T x) -> descltype (enable_if_t  {}) {
 retornar 0;
}

C ++ 17:

plantilla 
int calcular (T x) {
 si constexpr (suportAPI (T {})) {
   // només es compila si la condició és certa
   retornar x.Method ();
 } més {
   retornar 0;
 }
}

Això és molt convenient ja que el codi que pertany semànticament entre si no està dispers en diverses funcions. A més, fins i tot podeu definir lambdas que continguin if-constexpr.

Exemple 3: recollida d’un algoritme de temps de compilació

Sovint, heu de trobar el millor algorisme basat en un conjunt de regles i propietats d’un tipus. Hi ha moltes solucions. Per exemple, l'STL utilitza TypeTags per escollir l'algorisme adequat per a alguns iterators.

struct FooTag {};
struct BarTag {};
auto foldFF (...) {}
auto foldFB (...) {}
auto foldBF (...) {}
auto foldBB (...) {}
struct A {
 / * ... * /
 mitjançant tag = FooTag;
};
struct B {
 / * ... * /
 fent servir tag = BarTag;
};
plantilla 
plec automàtic (L l, R r, FooTag, BarTag) {foldFB (l, r); }
/ * més funcions de despatx * /
plantilla 
plec automàtic (L l, R r) {
 retorna el plec (l, r,
 nom del text L :: tag {},
 nom de lletra R :: tag {});
}

Tanmateix, un cop tingueu unes regles més complexes, potser necessitareu una solució més potent - SFINAE:

C ++ 14:

struct BazTag: FooTag, BarTag {};
plantilla  :: valor i&
 is_base_of  :: value
> plegar (L l, R r) {
 retornar el plecFB (l, r);
}

Amb C ++ 17 podeu descriure aquestes regles amb menys placa de calor i d’una manera més clara:

plantilla 
plec automàtic (L l, R r) {
 fent servir lTag = nom de lletra L ::
 utilitzant rTag = etiqueta de nom R:
if constexpr (is_base_of  :: valor) {
 if constexpr (is_same  :: valor) {
   retornar el plecFB (l, r);
 } més {
   retorna el plecBB (l, r);
 } més {
   retornar foldFF ();
 }
}

Això és molt pràctic, ja que treballar amb ifs és més intuïtiu que no pas fer servir diverses funcions del llenguatge.

La refactorització de les metafuncions esdevé tan senzilla com el codi ordinari. Amb if-constexpr, preocupació per sobrecàrregues ambigües i altres complicacions inesperades és cosa del passat.

Actualitzarem el nostre compilador tan aviat com Clang 3.9 sigui estable.