2013-03-28 13 views
6

Chcę wykonać przeciążone funkcje, które pobierają wspólny wskaźnik do klasy bazowej i klas pochodnych. Wydaje się działać dla referencji i surowych wskaźników, ale nie dla współdzielonych wskaźników w przypadku dodatkowej klasy pochodnej. Zobacz przykładowy kod:Funkcja przeciążania z niejednoznacznością argumentu współdzielonego wskaźnika

#include <memory> 

class Base{}; 
class Derived : public Base {}; 
class ExtraDerived : public Derived {}; 


bool IsBase(Base*){ return true; } 
bool IsBase(Derived*){ return false; } 

bool IsBase(std::shared_ptr<Base>){ return true; } 
bool IsBase(std::shared_ptr<Derived>){ return false; } 

int main() 
{ 
    auto derived = std::make_shared<Derived>(); 
    auto extra_derived = std::make_shared<ExtraDerived>(); 
    // works 
    auto raw_result_derived = IsBase(derived.get()); 
    auto raw_result_extra_derived = IsBase(extra_derived.get()); 
    auto shared_result_derived = IsBase(derived); 
    // doesn't work 
    auto shared_result_extra_derived = IsBase(extra_derived); 
} 

uzyskać: „error C2668:«IsBase»: niejednoznaczne wywołanie funkcji przeciążonej” przy użyciu programu Visual Studio 2012, ale również uzyskać ten sam wynik, gdy próbuję kod tutaj http://ideone.com/6uoa0p .

To nie wydaje się pożądane zachowanie (jak to działa na rzeczy "surowe"). Czy to jest ograniczenie szablonów, czy istnieje inny powód, dlaczego to nie działa lub jest to błąd? I jak mogę sprawić, żeby działało w najmniej brzydki sposób?

Najlepszym mogę wymyślić jest

//ugly workaround 
bool IsBase(std::shared_ptr<Base>, Base*){ return true; } 
bool IsBase(std::shared_ptr<Derived>, Derived*){ return false; } 
template<typename T> bool IsBase(std::shared_ptr<T> input) 
{ 
    return IsBase(input, input.get()); 
} 
+4

Dlaczego chcesz tego? Wystarczy tylko podać funkcję podstawową, z radością zaakceptuje pochodną wskaźnik klasy. Jeśli chcesz uchwycić pochodne obiekty klas, twoja metoda i tak nie zadziała, ponieważ wymagałoby to przeprowadzenia wysyłki na typ dynamiczny argumentu funkcji. Jeśli programista niejawnie przekształca 'Ptr ' na 'Ptr ' przed przejściem, to podejście przeładowania zepsuje się. –

+0

@ JohannesSchaub-litb Nie myślałem o tej sytuacji. Być może będę musiał przemyśleć to, co chcę. – Barabas

Odpowiedz

4

Czy to ograniczenie szablonów, czy istnieje inny powód, dlaczego to nie działa, czy jest to błąd?

Nie, to nie jest błąd. Rzeczywiście wydaje się, że trafiłeś w jedyną pułapkę inteligentnych wskaźników: std::shared_ptr<base> może być zbudowany z std::shared_ptr<derived>, a także z std::shared_ptr<extra_derived>, ale żadna z tych dwóch sekwencji konwersji nie jest lepsza niż druga (jako dwie zdefiniowane przez użytkownika sekwencje konwersji tego samego długość).

Jednak nadal można naprawić przeciążeń za pomocą pewnych ograniczeń SFINAE:

#include <type_traits> 

// Selected for `std::shared_ptr<Base>` 
template<typename T, typename std::enable_if< 
    std::is_same<T, Base>::value>::type* = nullptr> 
bool IsBase(std::shared_ptr<T>){ return true; } 

// Selected for `std::shared_ptr<T>` where T is a class derived from Base, 
// but not Base itself 
template<typename T, typename std::enable_if< 
    std::is_base_of<Base, T>::value && 
    !std::is_same<T, Base>::value 
    >::type* = nullptr> 
bool IsBase(std::shared_ptr<T>){ return false; } 
+0

Niestety to nie działa na VS2012, ale powinno to w przyszłości. Działa to w tym prostym przypadku, ale gdybym miał więcej przeciążeń dla różnych klas pochodnych, może to stanowić problem. Będę musiał zastanowić się, czy naprawdę chcę mieć takie funkcje, czy nie biorąc pod uwagę komentarza JohannesSchaub-litb na moje pytanie. – Barabas

Powiązane problemy