2012-11-15 27 views
11

Próbuję określić, która wersja funkcji składowej zostanie wywołana na podstawie parametru szablonu klasy. Próbowałem to:Wybieranie funkcji składowej przy użyciu różnych warunków enable_if

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct Point 
{ 
    void MyFunction(typename std::enable_if<std::is_same<T, int>::value, T >::type* = 0) 
    { 
    std::cout << "T is int." << std::endl; 
    } 

    void MyFunction(typename std::enable_if<!std::is_same<T, int>::value, float >::type* = 0) 
    { 
    std::cout << "T is not int." << std::endl; 
    } 
}; 

int main() 
{ 
    Point<int> intPoint; 
    intPoint.MyFunction(); 

    Point<float> floatPoint; 
    floatPoint.MyFunction(); 
} 

który myślałem mówi „użyć pierwszego MyFunction jeśli T jest int i użyć drugiego MyFunction jeśli T nie jest int, ale otrzymuję błędy kompilatora mówiąc:” Błąd: Nie Typ nazwie 'type' w 'struct std :: enable_if' ". Czy ktoś może wskazać, co robię źle tutaj?

+0

pokrewne Q & A: [ "Co się stało z moim SFINAE" (Redux)] (http://stackoverflow.com/questions/11531989/what-happened-to-my-sfinae-redux-conditional-template-class-members) – HostileFork

Odpowiedz

11

enable_if działa, ponieważ substitution of a template argument resulted in an error, i tak, że podstawienie jest usunięte z zestawu rozdzielczości przeciążenia i tylko inne możliwe przeciążenia są uwzględniane przez kompilator:

W twoim przykładzie nie ma podstawienia występujących ng podczas tworzenia instancji funkcji składowych, ponieważ argument szablonu T jest już znany w tym czasie. Najprostszym sposobem osiągnięcia tego, co próbujesz, jest utworzenie fałszywego argumentu szablonu, który jest domyślnie ustawiony na T i użycie go do wykonania SFINAE.

template<typename T> 
struct Point 
{ 
    template<typename U = T> 
    typename std::enable_if<std::is_same<U, int>::value>::type 
    MyFunction() 
    { 
    std::cout << "T is int." << std::endl; 
    } 

    template<typename U = T> 
    typename std::enable_if<std::is_same<U, float>::value>::type 
    MyFunction() 
    { 
    std::cout << "T is not int." << std::endl; 
    } 
}; 

Edit:

Jak HostileFork wymienia w komentarzach, oryginalny przykład pozostawia możliwość użytkownik jawnie określając argumenty szablon dla funkcji składowych i coraz błędny wynik. Poniższe czynności powinny uniemożliwić kompilowanie jawnych specjalizacji funkcji składowych.

template<typename T> 
struct Point 
{ 
    template<typename... Dummy, typename U = T> 
    typename std::enable_if<std::is_same<U, int>::value>::type 
    MyFunction() 
    { 
    static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!"); 
    std::cout << "T is int." << std::endl; 
    } 

    template<typename... Dummy, typename U = T> 
    typename std::enable_if<std::is_same<U, float>::value>::type 
    MyFunction() 
    { 
    static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!"); 
    std::cout << "T is not int." << std::endl; 
    } 
}; 
+0

W C++ 11 reguły SFINAE zostały nieco zmodyfikowane, dzięki czemu SFINAE nie wyzwala w przypadku typu zwrotu.Krótko mówiąc, ta odpowiedź jest błędna. – Nawaz

+0

@Nawaz Działa dobrze na gcc4.7.2, nie może opublikować linku demonstracyjnego, ponieważ LWS nie działa. [Tutaj] (https://ideone.com/J0j3IK) demo ideone. – Praetorian

+0

To nie jest dowód kodu zgodnego z C++ 11. – Nawaz

1

enable_if działa tylko dla wyprowadzona argumentów szablonu funkcji lub wyspecjalizowanych argumentów klasa szablonów. To, co robisz, nie działa, ponieważ oczywiście przy ustalonym T = int druga deklaracja jest po prostu błędna.

ten sposób można to zrobić:

template <typename T> 
void MyFreeFunction(Point<T> const & p, 
        typename std::enable_if<std::is_same<T, int>::value>::type * = NULL) 
{ 
    std::cout << "T is int" << std::endl; 
} 

// etc. 

int main() 
{ 
    Point<int> ip; 
    MyFreeFunction(ip); 
} 

Alternatywą byłoby specjalizują Point różnych rodzajów T, lub umieścić powyższą wolną funkcję do owinięcia zagnieżdżony szablon członkiem (co jest zapewne więcej "właściwe" rozwiązanie).

+0

Widziałem to rozwiązanie, ale wygląda na to, że naprawdę psuje czytelność kodu. –

+0

@DavidDoria: Oryginalny kod jest zbyt wymyślny, aby lepiej dopasować sugestię. –

+0

@DavidDoria Jeśli twoja sprawa polega na tym, że używasz SFINAE tylko do sprawdzenia, czy pewne typy to 'is_same' (wtedy domyślne, jeśli nie są takie same), to specjalizacja' Point' dla tych typów stała się właśnie tym chciał. Miałbyś taką samą instancję z bardziej czytelnymi definicjami. – HostileFork

2

Prostym rozwiązaniem jest użycie delegacji pracownika prywatne funkcje:

template<typename T> 
struct Point 
{ 

    void MyFunction() 
    { 
    worker(static_cast<T*>(0)); //pass null argument of type T* 
    } 

private: 

    void worker(int*) 
    { 
    std::cout << "T is int." << std::endl; 
    } 

    template<typename U> 
    void worker(U*) 
    { 
    std::cout << "T is not int." << std::endl; 
    } 
}; 

Kiedy T jest int pierwszy worker funkcja zostanie wywołana, ponieważ static_cast<T*>(0) okazuje się być typu int*. We wszystkich innych przypadkach zostanie wywołana szablonowa wersja pracownika.

+1

Rzeczywiście to lubię. Nie sądzę, bym kiedykolwiek go użył, ale przykład OP jest tak wymyślny, że to rzeczywiście dobre rozwiązanie. –

+1

static_cast (nullptr) –

0

podstawie sugestii pretorianów za (ale nie zmieniając typ zwracany funkcji), to wydaje się działać:

#include <iostream> 
#include <type_traits> 

template<typename T> 
struct Point 
{ 
    template<typename U = T> 
    void MyFunction(typename std::enable_if<std::is_same<U, int>::value, U >::type* = 0) 
    { 
    std::cout << "T is int." << std::endl; 
    } 

    template<typename U = T> 
    void MyFunction(typename std::enable_if<!std::is_same<U, int>::value, float >::type* = 0) 
    { 
    std::cout << "T is not int." << std::endl; 
    } 
}; 

int main() 
{ 
    Point<int> intPoint; 
    intPoint.MyFunction(); 

    Point<float> floatPoint; 
    floatPoint.MyFunction(); 
}