J'essaie de faire travailler un exemple simple pour comprendre comment l'utiliser std::enable_if
. Après avoir lu cette réponse , j'ai pensé qu'il ne devrait pas être trop difficile de trouver un exemple simple. Je veux utiliser std::enable_if
pour choisir entre deux fonctions membres et n'autoriser qu'une seule d'entre elles à être utilisée.
Malheureusement, ce qui suit ne se compile pas avec gcc 4.7 et après des heures et des heures d'essais, je vous demande quelle est mon erreur.
#include <utility>
#include <iostream>
template< class T >
class Y {
public:
template < typename = typename std::enable_if< true >::type >
T foo() {
return 10;
}
template < typename = typename std::enable_if< false >::type >
T foo() {
return 10;
}
};
int main() {
Y< double > y;
std::cout << y.foo() << std::endl;
}
gcc signale les problèmes suivants:
% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x enable_if.cpp -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'
Pourquoi g ++ ne supprime-t-il pas la mauvaise instanciation pour la deuxième fonction membre? Selon la norme, std::enable_if< bool, T = void >::type
n'existe que lorsque le paramètre de modèle booléen est vrai. Mais pourquoi g ++ ne considère-t-il pas cela comme SFINAE? Je pense que le message d'erreur de surcharge vient du problème que g ++ ne supprime pas la fonction du deuxième membre et pense que cela devrait être une surcharge.
std::is_same< T, int >::value
et! std::is_same< T, int >::value
qui donne le même résultat.Réponses:
SFINAE ne fonctionne que si la substitution dans la déduction d'argument d'un argument modèle rend la construction mal formée. Il n'y a pas de telle substitution.
C'est parce que lorsque le modèle de classe est instancié (ce qui se produit lorsque vous créez un objet de type
Y<int>
parmi d'autres cas), il instancie toutes ses déclarations de membres (pas nécessairement leurs définitions / corps!). Parmi eux se trouvent également ses modèles de membres. Notez queT
c'est connu alors, et!std::is_same< T, int >::value
donne faux. Il va donc créer une classeY<int>
qui contientL'
std::enable_if<false>::type
accès à un type non existant, de sorte que la déclaration est mal formée. Et donc votre programme est invalide.Vous devez faire en sorte que les modèles de membre
enable_if
dépendent d'un paramètre du modèle de membre lui-même. Ensuite, les déclarations sont valides, car tout le type est toujours dépendant. Lorsque vous essayez d'appeler l'un d'eux, la déduction d'arguments pour leurs arguments de modèle se produit et SFINAE se produit comme prévu. Voir cette question et la réponse correspondante sur la façon de procéder.la source
Y
serait utile: lorsqu'une instance de la classe modèle est instanciée, le compilateur ne compilera pas réellement les fonctions membres du modèle; cependant, le compilateur effectuera la substitution deT
dans les DÉCLARATIONS de modèle de membre afin que ces modèles de membre puissent être instanciés ultérieurement. Ce point de défaillance n'est pas SFINAE, car SFINAE s'applique uniquement lors de la détermination de l'ensemble des fonctions possibles pour la résolution de surcharge , et l'instanciation d'une classe n'est pas un cas de détermination d'un ensemble de fonctions pour la résolution de surcharge. (Ou alors je pense!)J'ai fait ce petit exemple qui fonctionne également.
Commentez si vous voulez que je développe. Je pense que le code est plus ou moins explicite, mais encore une fois, je l'ai fait pour que je me trompe peut-être :)
Vous pouvez le voir en action ici .
la source
error C4519: default template arguments are only allowed on a class template
.Q
, même si elle est égale àT
?test
fonction membre. Les deux ne peuvent pas exister en même temps.Q
transfère simplement le type de modèle de classeT
. Vous pouvez supprimer le modèle de classeT
comme ceci: cpp.sh/4nxw mais cela va à l' encontre de l'objectif.Pour les retardataires qui recherchent une solution qui "fonctionne juste":
Compiler avec:
Courir donne:
la source
std::enable_if_t
enresolvedType
.De ce post:
Mais on peut faire quelque chose comme ça:
la source
Une façon de résoudre ce problème, la spécialisation des fonctions membres est de placer la spécialisation dans une autre classe, puis d'hériter de cette classe. Vous devrez peut-être modifier l'ordre d'héritage pour accéder à toutes les autres données sous-jacentes, mais cette technique fonctionne.
L'inconvénient de cette technique est que si vous avez besoin de tester beaucoup de choses différentes pour différentes fonctions membres, vous devrez créer une classe pour chacune et l'enchaîner dans l'arborescence d'héritage. Cela est vrai pour accéder aux membres de données communs.
Ex:
la source
Le booléen doit dépendre du paramètre de modèle déduit. Donc, un moyen simple de corriger est d'utiliser un paramètre booléen par défaut:
Cependant, cela ne fonctionnera pas si vous souhaitez surcharger la fonction membre. Au lieu de cela, il est préférable d'utiliser
TICK_MEMBER_REQUIRES
la bibliothèque Tick :Vous pouvez également implémenter votre propre macro requiert une macro comme celle-ci (juste au cas où vous ne voudriez pas utiliser une autre bibliothèque):
la source
Voici mon exemple minimaliste, utilisant une macro. Utilisez des crochets doubles
enable_if((...))
lorsque vous utilisez des expressions plus complexes.la source