2016-10-01 9 views
9

Poniższy kod poprawnie sprawdza, czy typ T ma metodę sort. Ale kiedy modyfikuję linię oznaczoną (*) zmieniając decltype(&U::sort,...) na decltype(U::sort,...) (usunięto symbol &), kod zawsze zwraca false.SFINAE ze znakami ampersand przed funkcją o nazwie

Dlaczego?

Dlaczego sama nazwa nie jest wystarczająca? Co to oznacza &?

#include <iostream> 
#include <type_traits> 

template <typename T> 
class has_sort { 

    template <typename U> 
    static auto check(bool) -> decltype(&U::sort, std::true_type()); // (*) 

    template <typename U> 
    static std::false_type check(...); 

public: 
    using type = decltype(check<T>(true)); 
    static bool const value = type::value; 
}; 

int main() { 
    struct Foo { void sort(); }; 
    struct Foo2 { void sort2(); }; 
    std::cout << "Foo: " << has_sort<Foo>::value << std::endl; 
    std::cout << "Foo2: " << has_sort<Foo2>::value << std::endl; 
    std::cout << "int: " << has_sort<int>::value << std::endl; 
} 

Odpowiedz

3

Generalnie sprawdza się, czy typ U zawiera metodę elementu (statyczną lub nie) lub element danych (statyczny lub nie).
powodu, że będzie on pasował wszystkie następujące typy (i innych, jeśli wziąć pod uwagę również specyfikatorów):

  • struct Foo { void sort(); };
  • struct Foo { static void sort(); };
  • struct Foo { int sort; };
  • struct Foo { static int sort; };

Z drugiej strony, U::foo nie może być użyty do wykrywania metod elementów (nawet jeśli nadal używać go do wykrywania elementów danych w niektórych przypadkach).
W każdym razie, do swojej dyspozycji masz także template <typename U> static std::false_type check(...);, gdy próbujesz wykryć metodę składową sort, błąd podczas specjalizacji powyższej funkcji jest dyskretnie odrzucany z powodu reguł sfinae i ten jest pobierany.

Jeśli chcesz być bardziej surowe i wymagają sort być funkcję (zarówno statyczne lub nie), powinien zawierać nagłówek utility i używać std:: declval zamiast.W ten sposób, handlowe i nie jest już wymagane:

template <typename U> 
static auto check(bool) -> decltype(std::declval<U>().sort(), std::true_type()); // (*) 

ten sposób członków danych nazwy sort nie zostaną wykryte więcej.


Jeśli mogę dać sugestię, można uprościć nieco rzeczy za pomocą int/char przeciążenia i constexpr funkcje.
Jako przykład:

template <typename T> 
class has_sort { 
    template <typename U> 
    constexpr static auto check(int) -> decltype(std::declval<U>().sort(), std::true_type()) { return {}; } 

    template <typename U> 
    constexpr static std::false_type check(char) { return {}; } 

public: 
    static constexpr bool value = check<T>(0); 
}; 

Jeśli można użyć C++ 14, zmienne szablonu są jeszcze bardziej kompaktowe:

template<typename T, typename = void> 
constexpr bool has_sort = false; 

template<typename T> 
constexpr bool has_sort<T, decltype(std::declval<T>().sort(), void())> = true; 

Można go używać w swoim przykładzie, jak następuje:

std::cout << "Foo: " << has_sort<Foo> << std::endl; 
5

Odpowiedź jest prosta: nie można wziąć adres funkcji składowej bez &. Nie można spróbować samemu:

auto fun = Foo::sort; // error 

Norma wymaga, aby wskaźnik funkcji członek musi być używany z &, ponieważ składnia byłaby niejednoznaczna bez niego. Wyobraźmy sobie, że w szablonie:

template<typename T> 
void test() { 
    T::test2; // is it a member function pointer or static data member? 
} 

Więc check sfinae ma rację: bez &, kontrola będzie prawdziwa, jeśli typ T musiałby statyczny element danych o nazwie sort.

jednak można ominąć to ograniczenie z tej sztuczki, choć jest to w celach demonstracyjnych i nie będę porad można to zrobić:

struct Foo { 
    void sortImpl(); 

    static constexpr auto sort = &Foo::sortImpl; 
}; 

Następnie czek dla statycznego członka danych o nazwie sort będzie mieć rację, a sort będzie wskaźnikiem funkcji.

+0

Szkoda, że ​​nie mogę zaakceptować dwóch odpowiedzi. Nawiasem mówiąc, wersja bez ampersandów pasuje nie tylko do elementu danych (statycznego lub nie), ale także do statycznej metody. – olpa

+1

clang też to akceptuje i myślę, że jest poprawna. Standard mówi: "Statyczna funkcja członka (9.4) jest zwykłą funkcją." – olpa

+0

Rzeczywiście, sprawdzanie może być poprawne bez ampersand dla funkcji statycznych, ale jest wymagane dla niestatycznych funkcji składowych. –