Cos’è “Expression SFINAE”?

A http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx , il team VC ++ dichiara ufficialmente di non aver ancora implementato la funzione principale di C ++ 11 “Expression SFINAE”. Tuttavia, i seguenti esempi di codice copiati da http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html sono accettati dal compilatore VC ++.

Esempio 1:

template  struct A {}; char xxx(int); char xxx(float); template  A f(T){} int main() { f(1); } 

esempio 2:

 struct X {}; struct Y { Y(X){} }; template  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 

La mia domanda è: cos’è “Expression SFINAE”?

L’espressione SFINAE è spiegata abbastanza bene nella carta che hai collegato, credo. È SFINAE sulle espressioni. Se l’espressione dentro decltype non è valida, beh, calcia la funzione dalla sala VIP dei sovraccarichi. È ansible trovare la formulazione normativa alla fine di questa risposta.

Una nota su VC ++: non l’hanno implementata completamente . Su espressioni semplici, potrebbe funzionare, ma su altri, non lo farà. Vedi una discussione nei commenti su questa risposta per gli esempi che falliscono. Per renderlo semplice, questo non funzionerà:

 #include  // 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 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 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); } 

Con Clang, questo produce l'atteso:

Potrebbe chiamare con riferimento
Potrebbe chiamare con il puntatore
Imansible chiamare

Con MSVC, ottengo ... beh, un errore del compilatore:

 1> src \ main.cpp (20): errore C2995: '' test di tipo sconosciuto '(C, F)': il modello di funzione è già stato definito
 1> src \ main.cpp (11): vedere la dichiarazione di 'test'

Sembra inoltre che GCC 4.7.1 non sia all'altezza del compito:

 source.cpp: in sostituzione di 'template decltype ((c. * f (), void ())) test (C, F) [con C = X *;  F = void (X :: *) ()] ':
 source.cpp: 29: 17: richiesto da qui
 source.cpp: 11: 6: errore: imansible applicare il puntatore membro 'f' a 'c', che è di tipo non di class 'X *'
 source.cpp: in sostituzione di 'template decltype ((c. * f (), void ())) test (C, F) [con C = int;  F = int] ':
 source.cpp: 30: 16: richiesto da qui
 source.cpp: 11: 6: errore: 'f' non può essere utilizzato come puntatore membro, poiché è di tipo 'int'

Un uso comune di Expression SFINAE è quando si definiscono i tratti, come un tratto per verificare se una class ha una determinata funzione membro:

 struct has_member_begin_test{ template static auto test(U* p) -> decltype(p->begin(), std::true_type()); template static auto test(...) -> std::false_type; }; template struct has_member_begin : decltype(has_member_begin_test::test(0)) {}; 

Esempio dal vivo (Che, sorprendentemente, funziona di nuovo su GCC 4.7.1.)

Vedi anche questa mia risposta , che usa la stessa tecnica in un altro ambiente (alias senza tratti).


Formulazione normativa

§14.8.2 [temp.deduct]

p6 In alcuni punti del processo di deduzione degli argomenti del template è necessario prendere un tipo di funzione che faccia uso di parametri del template e sostituire quei parametri del template con gli argomenti template corrispondenti. Questo viene fatto all'inizio della deduzione degli argomenti del template quando qualsiasi argomento template esplicitamente specificato viene sostituito nel tipo di funzione, e nuovamente alla fine della deduzione degli argomenti del template quando vengono sostituiti tutti gli argomenti del template che sono stati dedotti o ottenuti dagli argomenti predefiniti .

p7 La sostituzione avviene in tutti i tipi ed espressioni utilizzati nel tipo di funzione e nelle dichiarazioni dei parametri del modello. Le espressioni includono non solo espressioni costanti come quelle che appaiono nei limiti degli array o come argomenti template nontype ma anche espressioni generali (cioè espressioni non costanti) all'interno di sizeof , decltype e altri contesti che consentono espressioni non costanti.

p8 Se una sostituzione produce un tipo o un'espressione non valida, digitare la deduzione non riesce. Un tipo o un'espressione non valida è uno che sarebbe mal formato se scritto usando gli argomenti sostituiti. [...]