L'expression SFINAE est très bien expliquée dans l'article que vous avez cité, je pense. Il s'agit de SFINAE sur les expressions. Si l'expression à l'intérieur de decltype
n'est pas valide, eh bien, renvoyez la fonction du salon VIP des surcharges. Vous pouvez trouver le libellé normatif à la fin de cette réponse.
Une note sur VC++ : Ils ne l'ont pas implémenté complètement . Sur des expressions simples, cela peut fonctionner, mais sur d'autres, ce ne sera pas le cas. Voir une discussion dans les commentaires sur cette réponse pour les exemples qui échouent. Pour faire simple, cela ne fonctionnera pas :
#include <iostream>
struct not_a_type{};
// catch-all case
void test(...)
{
std::cout << "Couldn't call\n";
}
// catch when C is a reference-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c.*f)(), void()) // 'C' is reference type
{
std::cout << "Could call on reference\n";
}
// catch when C is a pointer-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c->*f)(), void()) // 'C' is pointer type
{
std::cout << "Could call on pointer\n";
}
struct X{
void f(){}
};
int main(){
X x;
test(x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
Avec Clang, cette sortie est celle attendue :
Pourrait appeler avec la référence
Pourrait appeler avec un pointeur
Je n'ai pas pu appeler
Avec MSVC, j'obtiens... eh bien, une erreur de compilation :
1>src\\main.cpp(20): error C2995: ''unknown-type' test(C,F)' : function template has already been defined
1> src\\main.cpp(11) : see declaration of 'test'
Il semble également que GCC 4.7.1 ne soit pas tout à fait à la hauteur de la tâche :
source.cpp: In substitution of 'template decltype ((c.\*f(), void())) test(C, F) \[with C = X\*; F = void (X::\*)()\]':
source.cpp:29:17: required from here
source.cpp:11:6: error: cannot apply member pointer 'f' to 'c', which is of non-class type 'X\*'
source.cpp: In substitution of 'template decltype ((c.\*f(), void())) test(C, F) \[with C = int; F = int\]':
source.cpp:30:16: required from here
source.cpp:11:6: error: 'f' cannot be used as a member pointer, since it is of type 'int'
Une utilisation courante de l'expression SFINAE est la définition de traits, par exemple un trait permettant de vérifier si une classe possède une certaine fonction membre :
struct has_member_begin_test{
template<class U>
static auto test(U* p) -> decltype(p->begin(), std::true_type());
template<class>
static auto test(...) -> std::false_type;
};
template<class T>
struct has_member_begin
: decltype(has_member_begin_test::test<T>(0)) {};
Exemple concret. (Ce qui, étonnamment, fonctionne à nouveau sur GCC 4.7.1).
Voir aussi cette réponse de ma part qui utilise la même technique dans un autre environnement (c'est-à-dire sans traits).
Formulation normative :
§14.8.2 [temp.deduct]
p6 À certains moments du processus de déduction des arguments de modèle, il est nécessaire de prendre un type de fonction qui utilise des paramètres de modèle et de remplacer ces paramètres de modèle par les arguments de modèle correspondants. Ceci est fait au début de la déduction des arguments de modèle lorsque tout argument de modèle explicitement spécifié est substitué dans le type de fonction, et à nouveau à la fin de la déduction des arguments de modèle lorsque tous les arguments de modèle qui ont été déduits ou obtenus à partir d'arguments par défaut sont substitués. .
p7 La substitution se produit dans tous les types et expressions qui sont utilisés dans le type de fonction et dans les déclarations de paramètres de modèles. Les expressions comprennent non seulement les expressions constantes tels que ceux qui apparaissent dans les limites d'un tableau ou en tant qu'arguments de modèle non typés. mais aussi des expressions générales (c'est-à-dire des expressions non constantes) à l'intérieur de sizeof
, decltype
et d'autres contextes qui autorisent les expressions non constantes.
p8 Si une substitution donne lieu à un type ou à une expression invalide, la déduction de type échoue. Un type ou une expression invalide est une expression qui serait mal formée si elle était écrite en utilisant les arguments substitués. [...]