web-dev-qa-db-fra.com

Qu'est-ce que "Expression SFINAE"?

À http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx , l'équipe VC++ déclare officiellement qu'elle n'a pas encore implémenté le C++ 11 fonctionnalité principale "Expression SFINAE". Cependant, les exemples de code suivants copiés à partir de http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html sont acceptés par le compilateur VC++.

exemple 1:

template <int I> struct A {};

char xxx(int);
char xxx(float);

template <class T> A<sizeof(xxx((T)0))> f(T){}

int main()
{
    f(1);
}

exemple 2:

struct X {};
struct Y 
{
    Y(X){}
};

template <class T> auto f(T t1, T t2) -> decltype(t1 + t2); // #1
X f(Y, Y);  // #2

X x1, x2;
X x3 = f(x1, x2);  // deduction fails on #1 (cannot add X+X), calls #2

Ma question est: Qu'est-ce que "Expression SFINAE"?

56
xmllmx

L'expression SFINAE est assez bien expliquée dans l'article que vous avez lié, je pense. C'est SFINAE sur les expressions. Si l'expression à l'intérieur de decltype n'est pas valide, eh bien, lancez la fonction dans le salon VIP de 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 pourrait fonctionner, mais sur d'autres, cela ne fonctionnera pas. Voir une discussion dans les commentaires sur cette réponse pour des exemples qui échouent. Pour faire simple, cela ne fonctionnera pas:

#include <iostream>

// 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, cela génère les résultats attendus:

Pourrait appeler avec référence
Pourrait appeler avec un pointeur
Impossible d'appeler

Avec MSVC, j'obtiens ... enfin, une erreur de compilation:

 1> src\main.cpp (20): erreur C2995: '' test de type inconnu '(C, F)': le modèle de fonction a déjà été défini 
 1> src\main. cpp (11): voir déclaration de 'test' 

Il semble également que GCC 4.7.1 ne soit pas tout à fait à la hauteur:

 source.cpp: En remplacement de 'template decltype ((c. * f (), void ())) test (C, F) [avec C = X *; F = void (X :: *) ()] ': 
 Source.cpp: 29: 17: requis d'ici 
 Source.cpp: 11: 6: erreur: impossible d'appliquer le pointeur de membre' f 'à' c ', qui est de type non classe' X * '
 source.cpp: En remplacement de' template decltype ((c. * f (), void ())) test (C , F) [avec C = int; F = int] ': 
 Source.cpp: 30: 16: requis d'ici 
 Source.cpp: 11: 6: erreur:' f 'ne peut pas être utilisé comme pointeur de membre, car il est de type 'int' 

Une utilisation courante d'Expression SFINAE consiste à définir des traits, comme un trait pour 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 en direct. (ce qui, étonnamment, fonctionne à nouveau sur GCC 4.7.1.)

Voir aussi ma réponse , qui utilise la même technique dans un autre environnement (alias sans traits).


Libellé normatif:

§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. Cela se fait au début de la déduction des arguments de modèle lorsque des arguments de modèle spécifiés explicitement sont substitués dans le type de fonction, et à nouveau à la fin de la déduction des arguments de modèle lorsque des arguments de modèle déduits ou obtenus à partir des arguments par défaut sont substitués .

p7 La substitution se produit dans tous les types et expressions utilisés dans le type de fonction et dans les déclarations de paramètres de modèle. Les expressions incluent non seulement des expressions constantes telles que celles qui apparaissent dans les limites du tableau ou comme arguments de modèle non typés mais aussi générales expressions (c.-à-d. expressions non constantes) à l'intérieur sizeof, decltype, et d'autres contextes qui permettent des expressions non constantes.

p8 Si une substitution entraîne un type ou une expression non valide, la déduction de type échoue. Un type ou une expression non valide est un type qui serait mal formé s'il était écrit à l'aide des arguments substitués. [...]

70
Xeo