2013-03-28 6 views
6

Desidero rendere funzioni sovraccariche che accettano un puntatore condiviso a una classe di base e classi derivate. Sembra funzionare per i riferimenti e i puntatori grezzi, ma non per i puntatori condivisi nel caso di una classe derivata extra. Vedere il codice di esempio:Sovraccarico di funzioni con ambiguità dell'argomento del puntatore condiviso

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

ottengo: "Errore C2668: 'IsBase': chiamata ambigua alla funzione" quando si utilizza Visual Studio 2012, ma anche ottenere lo stesso risultato quando provo il codice a qui http://ideone.com/6uoa0p .

Questo non sembra il comportamento desiderato (in quanto funziona per roba "grezza"). Si tratta di una limitazione del modello, c'è un altro motivo per cui questo non funziona o è un bug? E come posso farlo funzionare nel modo meno brutto?

Il meglio che posso venire in mente è

//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

Perché vuoi questo? Fornisci solo la funzione di base, accetterà felicemente il puntatore della classe derivata. Se vuoi catturare oggetti di classe derivati, il tuo metodo non funzionerà comunque perché avrebbe bisogno di inviare il tipo dinamico dell'argomento della funzione per farlo correttamente. Se il programmatore converte implicitamente il 'Ptr ' in un 'Ptr ' prima di passare, questo approccio di sovraccarico si rompe. –

+0

@ JohannesSchaub-litb Non ho pensato a quella situazione. Potrei dover ripensare quello che voglio. – Barabas

risposta

4

Si tratta di una limitazione del template, c'è un altro motivo per cui questo non funziona o è un bug?

No, non è un bug. In effetti sembra che tu abbia colto l'unica trappola dei puntatori intelligenti: un std::shared_ptr<base> può essere costruito da std::shared_ptr<derived> e da std::shared_ptr<extra_derived>, ma nessuna di queste due sequenze di conversione è migliore dell'altra (essendo due sequenze di conversione definite dall'utente dello stesso lunghezza).

Tuttavia, è ancora possibile risolvere il tuo sovraccarichi utilizzando alcuni vincoli 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

Purtroppo questo non funziona su VS2012, ma dovrebbe avvenire in futuro. Funziona per questo semplice caso, ma se avessi più sovraccarichi per diverse classi derivate potrebbe diventare un problema. Dovrò pensare se effettivamente voglio avere funzioni come questa o non considerando il commento di JohannesSchaub-litb sulla mia domanda. – Barabas