• [^] # Re: Je m'y colle...

    Posté par (site web personnel) . En réponse à la dépêche C++17 branche à la compilation (`if constexpr`). Évalué à 5.

    Le SFINAE est un outil qui sert à détecter des informations non triviales ou des possibilités par essai et erreur. Sauf que les erreurs sont ignorées et à la place on se débrouille pour récupérer un truc que l'on va assimiler à faux. Ce booléen on s'en sert ensuite ailleurs pour choisir quelle spécialisation nous intéresse.

    Exemple à la C++98

    template <typename T>
    struct has_typedef_montype
    {
     using yes = char[1];
     using no = char[2];
     // Signatures qui renvoient des choses différentes
     // - Version qui prend ce que l'on cherche
     template <typename C> static yes& test(typename C::montype*);
     // - Version qui prend n'importe qui
     template <typename C> static no & test(...);
     // NB, on n'a pas besoin de définition, mais de renvoyer une réf.
     // Ici le compilo tente de résoudre. 
     // Si T permet d'extraire un type `montype*`, alors on renvoie `yes` qui a une taille de 1
     // Sinon, on défaulte sur la version variadique qui renvoie un `no` de taille 2
     // Ce qui permet de construire le booléen `value` grâce à sizeof qui est toujours résolu à la compilation
     static const bool value = sizeof(test<T>(NULL))==sizeof(yes);
    };
    struct U { using montype = int; };
    int main ()
    {
     std::cout << std::boolalpha; // affiche true ou false
     std::cout << has_typedef_montype<int>::value << "\n";
     std::cout << has_typedef_montype<U>::value << "\n";
    }

    Façon C++14 (voire 17), on a de nouveaux outils:

    #if 0
    // C++17
    // Requiert gcc 5.0
    template <typename...T> using void_t = void;
    #else
    // Pour gcc v < 5.0
    // Until CWG 1558 (a C++14 defect), unused parameters in alias templates
    // were not guaranteed to ensure SFINAE and could be ignored, so earlier
    // compilers require a more complex definition of void_t, such as
    template<typename... Ts> struct make_void { typedef void type;};
    template<typename... Ts> using void_t = typename make_void<Ts...>::type;
    #endif
    // Cas par défaut
    template <typename T, typename = void>
    struct has_typedef_montype
    : std::false_type
    {};
    // Cas où l'on arrive à trouve le typedef qui nous intéresse
    template <typename T>
    struct has_typedef_montype<T, void_t<typename T::montype>>
    : std::true_type
    {};