2012-09-29 13 views
50

W http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx zespół VC++ oficjalnie oświadcza, że ​​nie wdrożył jeszcze funkcji rdzeniowej C++ 11 "Expression SFINAE". Jednak Poniższe przykłady kodu skopiowane z http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html są akceptowane przez kompilator VC++.Co to jest "Expression SFINAE"?

Przykład 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); 
} 

Przykład 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 

Moje pytanie brzmi: Co to jest "Ekspresja SFINAE"?

+0

Dlaczego nie spojrzeć w oczywisty: http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error – SChepurin

+5

@SChepurin: To wyjaśnia normalne SFINAE , nie wyrażenie SFINAE. Ale to dobry punkt. Czy OP w ogóle wie o SFINAE? – Xeo

+1

@Xeo, Tak, znam SFINAE w ogóle. Wielkie dzięki dla twojego ilustracyjnego wyjaśnienia. – xmllmx

Odpowiedz

62

Wyrażenie SFINAE jest wyjaśnione całkiem dobrze w moim pomyśle. To SFINAE w wyrażeniach. Jeśli wyrażenie wewnątrz decltype nie jest prawidłowe, dobrze, kopnij funkcję z salonu VIP przeciążenia. Możesz znaleźć normatywne sformułowanie na końcu tej odpowiedzi.

Uwaga na temat VC++: nie zostały one wdrożone całkowicie. W prostych wyrażeniach może działać, ale na innych nie. Zobacz dyskusję w komentarzach on this answer dla przykładów, które zawiodły. Żeby było proste, to nie będzie działać:

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

Z Clang to wyprowadza z oczekiwaniami:

Could call with reference
Could call with pointer
Couldn't call

Z MSVC, otrzymuję ... cóż, błąd kompilatora:

 
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' 

wydaje się również, że GCC 4.7.1 nie jest całkiem do zadania:

 
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' 

Powszechnym zastosowaniem Expression SFINAE jest przy definiowaniu cech, jak cechy, by sprawdzić, czy klasa sport pewną funkcję użytkownik: (. Które nieoczekiwanie znowu działa na GCC 4.7.1)

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)) {}; 

Live example.

Zobacz także this answer of mine, która używa tej samej techniki w innym środowisku (również bez cech).


normatywna treść:

§14.8.2 [temp.deduct]

p6 At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.

p7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) insidesizeof , decltype, and other contexts that allow non-constant expressions.

p8 If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed if written using the substituted arguments. [...]

+0

Zastanawiam się, dlaczego nagle pojawiły się linki do liveworkspace zamiast ideone, gdy zauważyłem, że liveworkspace używa gcc 4.7.2. Ach! Wreszcie! –

+2

Link na żywo jest uszkodzony :( –