2010-11-11 31 views
11

Dans le code:Comment utiliser enable_if pour activer les fonctions membres en fonction des paramètres du modèle de classe

template<class T> 
struct is_builtin 
{ 
    enum {value = 0}; 
}; 

template<> 
struct is_builtin<char> 
{ 
    enum {value = 1}; 
}; 

template<> 
struct is_builtin<int> 
{ 
    enum {value = 1}; 
}; 

template<> 
struct is_builtin<double> 
{ 
    enum {value = 1}; 
}; 

template<class T> 
struct My 
{ 
    typename enable_if<is_builtin<T>::value,void>::type f(T arg) 
    { 
     std::cout << "Built-in as a param.\n"; 
    } 


    typename enable_if<!is_builtin<T>::value,void>::type f(T arg) 
    { 
     std::cout << "Non - built-in as a param.\n"; 
    } 
}; 

struct A 
{ 
}; 

int main() 
{ 
    A a; 
    My<int> m; 
    My<A> ma; 
    m.f(1); 
    ma.f(a); 
    return 0; 
} 

Je reçois une erreur:

error C2039: 'type' : is not a member of 'std::tr1::enable_if<_Test,_Type>'  

Il est évident que je ne comprends pas comment utiliser enable_if. Ce que je pensais était que je peux activer une ou une deuxième fonction membre à partir d'un ensemble de fonctions membres pendant la compilation, mais cela ne fonctionne pas. Quelqu'un pourrait-il m'expliquer comment le faire correctement?
Edité
Ce que je ne comprends vraiment pas pourquoi n'est pas là typedef dans un de ces def. Le compilateur ne peut pas le trouver et ne le compilera pas.

Répondre

13

Vous ne pouvez pas utiliser les paramètres de modèle de classe pour obtenir SFINAE pour les fonctions de membre.

Vous devez soit

  • faire membre fonction d'un modèle de fonction membre à la place et utiliser enable_if sur les paramètres du modèle du modèle de fonction membre ou

  • déplacer la fonction membre f dans une classe politique et spécialisez le modèle de classe en utilisant enable_if.

+0

pourrait vous fournir un exemple s'il vous plaît? –

+0

@Il y a un exemple d'utilisation de la seconde approche (spécialisation d'un template de classe) dans [la documentation Boost 'enable_if'] (http://beta.boost.org/doc/libs/1_44_0/libs/utility/enable_if .html) (voir la section 3.1). –

+0

merci pour le lien. Je vais le lire maintenant. –

-2

enable_if attend une métafonction. Pour utiliser un bool, vous avez besoin de enable_if_c. Je suis surpris que vous n'obteniez pas d'erreurs expliquant ce problème.

Vous pouvez corriger votre métafonction en déclarant un typedef 'type' à l'intérieur qui est simplement lui-même. Ensuite, vous pouvez utiliser boost::enable_if<is_builtin<T>>::type

+1

J'utilise std :: enable_if –

0

Voici comment cela fonctionne (notez que pour plus de commodité, j'ai remplacé votre trait is_builtin avec std::is_arithmetic et utilisé plus des choses 11 C++, mais il fonctionne quelque façon):

template<class T> 
struct My 
{ 
    template<typename T_ = T, std::enable_if_t<std::is_arithmetic<T_>::value>* = nullptr> 
    void f(T_ arg) 
    { 
     std::cout << "Built-in as a param.\n"; 
    } 

    template<typename T_ = T, std::enable_if_t<!std::is_arithmetic<T_>::value>* = nullptr> 
    void f(T_ arg) 
    { 
     std::cout << "Non - built-in as a param.\n"; 
    } 
}; 

DEMO

La partie cruciale est d'amener le paramètre de modèle dans le contexte immédiat en utilisant un paramètre de modèle de fonction par défaut T_ qui est égal au paramètre de modèle de classe T. Pour plus de détails, voir this question.

1

Vous pouvez corriger votre code en utilisant modifié enable_if

template < typename T > 
struct __Conflict {}; 

template <bool B, class T = void> 
struct __enable_if { typedef __Conflict<T> type; }; 

template <class T> 
struct __enable_if<true, T> { typedef T type; }; 

Exemple d'utilisation:

template <typename T> 
class Lazy 
{ 
public: 
    void _ctor(bool b); 
    void _ctor(typename __enable_if<!std::is_same<T, bool>::value, T>::type); 
}; 

template <typename T> 
void Lazy<T>::_ctor(bool b) 
{ 
    std::cout << "bool " << b << std::endl; 
}; 

template <typename T> 
void Lazy<T>::_ctor(typename __enable_if<!std::is_same<T, bool>::value, T>::type t) 
{ 
    std::cout << "T " << t << std::endl; 
}; 

int main(int argc, char **argv) 
{ 
    Lazy<int> i; 
    i._ctor(10); 
    i._ctor(true); 

    Lazy<bool> b; 
    b._ctor(true); 

    return 0; 
} 
+0

+1, bien que j'essaie généralement d'éviter de commencer les noms avec des traits de soulignement car il existe des règles dans lesquelles un nom commençant par un ou deux caractères de soulignement est réservé dans certains cas. Aussi, y at-il une raison pour laquelle vous avez nommé une fonction '_ctor' au lieu de simplement définir un constructeur? – SirGuy

+0

comme je me souviens soulignés sont utilisés pour montrer l'implémentation locale de quelque chose, _ctor est juste le nom de la méthode dans mon code que je n'ai pas pris la peine de renommer – Alexander77

+0

De [cppreference] (http://en.cppreference.com/w/ cpp/keyword): De même, ** tous les identifiants qui contiennent un double trait de soulignement __ dans n'importe quelle position ** et chaque identifiant commençant par un trait de soulignement suivi d'une lettre majuscule sont toujours réservés et tous les identifiants commençant par un trait de soulignement sont réservés utiliser comme noms dans l'espace de noms global. Voir les identifiants pour plus de détails. _ Donc, à tout le moins, votre modèle '__enable_if' devrait être renommé pour s'assurer qu'il n'interfère jamais avec les détails d'implémentation de la bibliothèque standard. – SirGuy