web-dev-qa-db-fra.com

Sélectionnez le constructeur de classe à l'aide de enable_if

Considérez le code suivant:

#include <iostream>
#include <type_traits>

template <typename T>
struct A {
    int val = 0;

    template <class = typename std::enable_if<T::value>::type>
    A(int n) : val(n) {};
    A(...) { }

    /* ... */
};

struct YES { constexpr static bool value = true; };
struct NO { constexpr static bool value = false; };

int main() {
    A<YES> y(10);
    A<NO> n;
    std::cout << "YES: " << y.val << std::endl
              << "NO:  " << n.val << std::endl;
}

Je veux définir sélectivement le constructeur A :: A (int) uniquement pour certains types utilisant enable_if. Pour tous les autres types, il y a le constructeur par défaut A :: A (...) qui devrait être le cas par défaut pour le compilateur lorsque la substitution échoue. Cependant, cela a du sens pour moi, le compilateur (version gcc 4.9.0 20130714) se plaint toujours

sfinae.cpp: Dans l'instanciation de 'struct A': sfinae.cpp: 19: 11:
requis d'ici sfinae.cpp: 9: 5: erreur: aucun type nommé 'type' dans
'struct std :: enable_if'
A (int n): val (n) {};

Est-ce que quelque chose comme ça est possible pour le constructeur? Est-ce possible avec un ou plusieurs autres constructeurs (constructeur de copie et constructeur de déplacement)?

44
tomas789

Je pense que cela ne peut pas fonctionner avec un seul paramètre de modèle par défaut, car sa valeur doit être résolue lorsque le modèle de classe est instancié.

Nous devons reporter la substitution au point d'instanciation du modèle constructeur. Une façon consiste à définir par défaut le paramètre de modèle sur T et à ajouter un paramètre factice supplémentaire au constructeur:

template<typename U = T>
A(int n, typename std::enable_if<U::value>::type* = 0) : val(n) { }
37
jrok

Habituellement, cela se fait à l'aide d'un argument anonyme par défaut:

A(int n, typename std::enable_if<T::value>::type* = 0) : val(n) {};

Vous ne pouvez pas utiliser les paramètres de modèle de la classe vers les méthodes SFINAE out. SO une façon consiste à ajouter un type factice remplaçant int:

voir: http://ideone.com/2Gnyzj

#include <iostream>
#include <type_traits>

template <typename T>
struct A {
    int val = 0;

    template<typename Integer
            ,typename  = typename std::enable_if<T::value && sizeof(Integer)>::type
            >
    A(Integer n) : val(n) {};

    A(...) {}
    /* ... */
};

struct YES { constexpr static bool value = true; };
struct NO { constexpr static bool value = false; };

int main() {
    A<YES> y(10);
    A<NO> n;
    std::cout << "YES: " << y.val << std::endl
              << "NO:  " << n.val << std::endl;
}

Cela fonctionne parce que vous utilisez un paramètre de modèle de membre pour SFINAE sur le constructeur, mais le test est toujours vrai, donc il ne pollue pas vos vérifications

9
Joel Falcou