Alternatives évaluées à la compilation – if constexpr

Ce qui suit est une ébauche d'un article à venir.

À partir de C++ 17, il devient possible d'évaluer des conditions à la compilation et d'exclure, sur la base de ces conditions, des blocs de code (qui doivent toutefois être bien formés). Par exemple :

//
// On veut un array<T,N> si N*sizeof(T) est moins de SEUIL bytes, et un vector<T> de N éléments sinon,
// ce qui explique le type de retour... qui sera déterminé par if constexpr
//
template <class T, int N, int SEUIL = 4096>
   auto creer_tampon_temporaire() {
      if constexpr(NB * sizeof(T) < SEUIL)
         return array<T,N> {};
      else
         return vector<T>(N);
   }

Ceci permet entre autres de remplacer plusieurs recours à enable_if, qui est quelque peu obscur, par du code bien plus simple à comprendre et à expliquer :

Avec enable_if Avec if constexpr
template <class T, int N, int SEUIL = 4096>
   enable_if_t<(sizeof(T) < SEUIL), array<T, N>> creer_tampon_temporaire() {
      return {};
   }
template <class T, int N, int SEUIL = 4096>
   enable_if_t<(sizeof(T) >= SEUIL), vector<T>> creer_tampon_temporaire() {
      return vector<T>(N);
   }
template <class T, int N, int SEUIL = 4096>
   auto creer_tampon_temporaire() {
      if constexpr(NB * sizeof(T) < SEUIL)
         return array<T,N> {};
      else
         return vector<T>(N);
   }
template <class T>
   enable_if_t<(!is_floating_point_v<T>), bool> assez_proches(T a, T b) {
      return a == b;
   }
template <class T>
   enable_if_t<(is_floating_point_v<T>), bool> assez_proches(T a, T b) {
      return abs(a - b) <= static_cast<T>(0.000001);
   }
template <class T>
   bool assez_proches(T a, T b) {
      if constexpr(is_floating_point_v<T>)
         return abs(a - b) <= static_cast<T>(0.000001);
      else
         return a == b;
   }

C'est simple, direct et extrêmement utile.

Comme le fait remarquer Nicolai Josuttis (propos rapportés par Peter Sommerlad dans https://twitter.com/PeterSommerlad/status/1034726303440748544), seule la condition doit être évaluable à la compilation; il est possible de profiter de la partie initialisation à l'exécution, ce qui permet par exemple d'écrire :

template <class C, class M>
   auto process_synchronized(const C &c, M &mut) {
      if constexpr(lock_guard _ { mut }; is_pointer_v<typename C::value_type>) {
         // opérer de manière synchronisée sur les éléments de c (qui sont des pointeurs)
      } else {
         // opérer de manière synchronisée sur les éléments de c (qui ne sont pas des pointeurs)
      }
   }

Lectures complémentaires

Quelques liens pour enrichir le propos.


Valid XHTML 1.0 Transitional

CSS Valide !