2017-01-16 1 views
5

J'essaie d'obtenir une meilleure compréhension de std::enable_if en C++ 11 et j'ai essayé d'écrire un exemple minimal: une classe A avec une fonction membre void foo() qui a des différences Implémentations basées sur le type T à partir du modèle de classe.
Le code ci-dessous donne le résultat souhaité, mais je ne comprends pas encore complètement. Pourquoi la version V2 fonctionne-t-elle, mais pas V1? Pourquoi le type "redondant" U est-il requis?enable_if: exemple minimal pour la fonction membre vide sans arguments

#include <iostream> 
#include <type_traits> 

template <typename T> 
class A { 

    public: 

     A(T x) : a_(x) {} 

     // Enable this function if T == int 
     /* V1 */ // template <   typename std::enable_if<std::is_same<T,int>::value,int>::type = 0> 
     /* V2 */ template <typename U=T, typename std::enable_if<std::is_same<U,int>::value,int>::type = 0> 
     void foo() { std::cout << "\nINT: " << a_ << "\n"; } 

     // Enable this function if T == double 
     template <typename U=T, typename std::enable_if<std::is_same<U,double>::value,int>::type = 0> 
     void foo() { std::cout << "\nDOUBLE: " << a_ << "\n"; } 

    private: 

     T a_; 

}; 

int main() { 
    A<int> aInt(1); aInt.foo(); 
    A<double> aDouble(3.14); aDouble.foo(); 
    return 0; 
} 

Y at-il une meilleure façon d'obtenir le résultat souhaité, à savoir pour avoir différentes implémentations d'une fonction void foo() en fonction d'un paramètre de modèle de classe?

+0

Votre exemple n'est pas une utilisation appropriée de 'enable_if'. Une surcharge simple résoudrait votre cas. 'enable_if' est surtout utile sur un paramètre de template * deduced *. –

+0

L'utilisation de 'std :: enable_if' serait appropriée pour séparer les types déduits * en virgule flottante * des types, par exemple, * integral *. Deux types spécifiques comme celui-ci seraient mieux adaptés à * la surcharge *. – WhozCraig

+0

@KerrekSB @WhozCraig Comment pourrais-je surcharger dans ce cas particulier? En utilisant une définition hors-classe 'void A :: foo() {}' et 'void A :: foo() {}'? Mon intention serait que le code final contienne uniquement les versions de la fonction qui sont requises (ie pas de fonction 'A :: foo()' si une telle fonction n'est jamais appelée) – untergam

Répondre

2

Je sais que cela ne répondra pas entièrement à votre question, mais il pourrait vous donner plus d'idées et de la compréhension de la façon dont vous pouvez utiliser std::enable_if.

Vous pouvez remplacer vos fonctions membres foo avec les éléments suivants et ont des fonctionnalités identiques:

template<typename U=T> typename std::enable_if<std::is_same<U,int>::value>::type 
foo(){ /* enabled when T is type int */ } 

template<typename U=T> typename std::enable_if<std::is_same<U,double>::value>::type 
foo(){ /* enabled when T is type double */ } 

Un temps, j'ai acquis une très bonne compréhension de la façon dont fonctionne enable_if, mais malheureusement, je l'ai oublié la plupart de ses subtilités et souvenez-vous simplement des façons plus pratiques de l'utiliser.

+0

Je pense que vous devez également supprimer le 'void' avant la fonction car il est maintenant déjà inclus dans le' std :: enable_if <> '. Mais merci pour cette contribution, m'a aidé à faire un pas de plus pour comprendre ce qui se passe. Diriez-vous que c'est un moyen légitime d'atteindre le polymorphisme de compilation? – untergam

+0

@untergam Oups, belle prise! Je vais l'éditer maintenant.Je pense que c'est une façon légitime de faire les choses, je ne suis pas sûr s'il y a de meilleures façons de le faire –

0

Comme pour la première question: pourquoi V1 ne fonctionne pas? SFINAE s'applique uniquement en résolution de surcharge - V1 provoque cependant une erreur au point où le type A est instancié, bien avant la résolution de surcharge foo().

Je suppose qu'il y a beaucoup d'implémentations possibles - ce qui est le plus approprié dépend d'un cas réel en question. Une approche courante serait de différer la partie de A qui est différente pour différents types de modèles à une classe d'assistance.

template <typename T> 
class A_Helper; 

template <> 
class A_Helper<int> { 
public: 
    static void foo(int value){ 
     std::cout << "INT: " << value << std::endl; 
    } 
}; 

template <> 
class A_Helper<double> { 
public: 
    static void foo(double value){ 
     std::cout << "DOUBLE: " << value << std::endl; 
    } 
}; 

template <typename T> 
class A { 
public: 

    A(T a) : a_(a) 
    {} 

    void foo(){ 
     A_Helper<T>::foo(a_); 
    } 

private: 
    T a_; 
}; 

Le reste de A peut être déclaré qu'une fois de façon générique - seules les parties qui diffèrent sont reportés à une aide. Il ya beaucoup de variations possibles à ce sujet - en fonction de vos besoins ...

+0

Merci @j_kubik pour votre réponse. Je vois comment cette solution avec des classes auxiliaires modélisées fonctionnerait, mais c'est exactement le genre de bourrage de code que j'essayais d'éviter en premier lieu. Je préférerais une version minimale avec 'enable_if' ou au moins avec une syntaxe similaire. – untergam