2013-05-22 19 views
7

Mam funkcję, która może akceptować dowolny typ według uniwersalnego odwołania i chciałbym ją przeciążać dla określonych typów (niektóre z nich sami są szablonami, chociaż nie sądzę, że jest to ważne). Niestety, wydaje mi się, że nie udało mi się usunąć przeciążeń w odpowiedniej kolejności.Rozdzielczość przeciążeniowa z odwołaniami uniwersalnymi

Przypuszczam, że druga deklaracja foo byłaby preferowana, ponieważ jest bardziej szczegółowa (mniej szablonowa), chociaż wygląda na to, że brakuje mi nieco zrozumienia dla rozwiązania dotyczącego przeciążenia. Co ciekawe, zmiana drugiej deklaracji na wartość X według wartości powoduje, że jest drukowana "dobra, dobra", co powoduje, że jest ona "niezła, dobra". Oczywiście usunięcie pierwszej deklaracji całkowicie powoduje, że zwraca ona "dobry, dobry", ponieważ nie ma innego wyboru.

Dlaczego tak się dzieje? A co najważniejsze, jeśli poniższy kod nie działa, jak można przeciążać funkcję tym podpisem?

#include <iostream> 
#include <string> 

class X {}; 

template<typename T> 
inline std::string foo(T && rhs) { 
    return "bad"; 
} 

inline std::string foo(const X & rhs) { 
    return "good"; 
} 

int main() { 
    std::cout << foo(X()) << std::endl; 
    X x; 
    std::cout << foo(x) << std::endl; 
    return 0; 
} 

Edit:

Może bardziej rondo Rozwiązaniem tego problemu jest zrobić to w sposób pośredni. Pozbądź się pierwszej formy foo i użyj SFINAE, aby sprawdzić, czy istnieje przeciążenie, ale nie zadzwoń pod foo_fallback.

+0

mam wymyślić rozwiązanie przeciążenia części: http://mortoray.com/2013/06/03/overriding-the-broken-universal-reference-t/ –

+1

1 + godzina [youtube video] (https://www.youtube.com/watch?v=T5swP3dr190) w sprawie tego problemu, który z łatwością łączy się z tym pytaniem. – Yakk

Odpowiedz

3

Aby odpowiedzieć na question.comment aby odpowiedzieć Kerre jest, można spróbować użyć SFINAE:

#include <type_traits> 
#include <string> 

template <class T> 
struct HasFooImpl_ { 
    template <typename C> 
    static std::true_type test(decltype(fooImpl(std::declval<C>()))*); 
    template <typename C> 
    static std::false_type test(...); 
    typedef decltype(test<T>(0)) type; 
}; 

template <typename T> 
using HasFooImpl = typename HasFooImpl_<T>::type; 

template <typename T> 
typename std::enable_if<HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return fooImpl(std::forward<T>(t)); 
} 

template <typename T> 
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return "generic!"; 
} 

Trzeba by realizować funkcję fooImpl dla każdego typu, które nie chcą być traktowane genericly .

Wdrożenie było nieco trudne, najpierw próbowałem po prostu enable_if<is_same<string, decltype(fooImpl(declval<C>()))>::value, ale w odpowiedzi na awarię !is_same<>::value dało mi błędy kompilatora, ponieważ próbowało również utworzyć egzemplarz decltype.

Ta implementacja ma jedno zastrzeżenie, że może, ale nie chcą używać: jeśli T jest cabrio do innego typu, który ma fooImpl zdefiniowany, że konwersja będzie kopać w

Można zobaczyć całość. rzeczą w akcji tutaj: http://ideone.com/3Tjtvj

Aktualizacja: jeśli nie chcesz, aby typ konwersji, to faktycznie staje się łatwiejsze:

#include <type_traits> 
#include <string> 

template <typename T> void fooImpl(T); 

template <typename T> 
using HasFooImpl = typename std::is_same<std::string, decltype(fooImpl(std::declval<T>()))>; 

template <typename T> 
typename std::enable_if<HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return fooImpl(std::forward<T>(t)); 
} 

template <typename T> 
typename std::enable_if<!HasFooImpl<T>::value, std::string>::type 
foo(T&& t) 
{ 
    return "generic!"; 
} 

Zobacz http://ideone.com/miaoop

+0

Podoba mi się to. [Tutaj jest] (http://ideone.com/vkyScJ) kolejna implementacja tego samego pomysłu, ale użycie wyrażenia SFINAE zamiast klasy pomocniczej i 'enable_if' (wadą jest to, że wymaga innego poziomu niezależności). – jleahy

+0

Właściwie wolę semantykę konwersji, a implementacja jest bardzo zgrabna. Doskonały. – jleahy

2

Konwersja z X do const X jest uważany za gorszy niż bezpośrednim meczu przeciążenia matrycy z T = X lub T = X &.

+0

Jak zatem sprawić, by uniwersalna wersja referencyjna zajęła miejsce tylne dla bardziej konkretnych wersji? – jleahy

+2

Chciałbyś też zjeść lunch w pewnym momencie? – jleahy

+0

@jleahy: Możesz zrobić * trzy * niesformowane przeciążenia dla 'X &', 'X const &' i 'X &&', aby pokryć wszystkie przypadki ... i tak :-) Edytuj: [patrz tutaj] (http : //ideone.com/KNqnRB). –

Powiązane problemy