2014-11-28 15 views
6

Mam klasy szablon, który wymaga, aby móc porównać między dwoma obiektami, poprzez obiektów porównawczych pochodzących z klasy Compare mam:Warunkowo egzekwować rodzaje szablonów w C++

template<typename T> 
class Container { 
public: 
    template<typename A, typename B> 
    class Compare { 
    public: 
     virtual bool eq(const A&, const B&) const = 0; 
    }; 

ja zapewnić obiekty porównawcze domyślna zakładając typ T ma operatora ==:

template<typename A, typename B> 
    class Default : public Compare<A,B> { 
    public: 
     bool eq(const A& a, const B& b) const { return a==b; } 
    }; 
private: 
    Compare<T,T>* comparison_object; 
    bool uses_default; 
    Container() : comparison_object(new Default<T,T>()), uses_default(true) {} 
    Container(Compare<T,T>& cmp) : comparison_object(&cmp), uses_default(false) {} 
    ~Container() { if(uses_default) delete comparison_object; } 
}; 

jednak gdy próbuję skompilować to z klasą niestandardową nie mieć operator== przeciążenie (nawet jeśli stanowią przedmiot pochodzący z Compare):

MyObjCmp moc; 
Container<MyObj>(&moc); 

Kompilator skarży się, że operator nie istnieje:

error: no match for 'operator==' (operand types are 'const MyObj' and 'const MyObj') 

Ma to sens, ponieważ klasa Default musi jeszcze być stworzonym, chociaż go nie potrzebuję. Ale teraz potrzebuję obejścia ...

Jakieś pomysły?

+0

'Porównaj * compare_object;' Powoduje to przeciek pamięci. użyj 'std :: unique_ptr' lub' std :: shared_ptr', zamiast używać 'new' /' delete' bezpośrednio – ikh

+1

My destructor się tym zajmuje ... Mam "default_created" flagę logiczną i warunkową delecję, don nie martw się :) – Dori

+0

@Niall, będę edytować ... Myślałem, że to nie ma znaczenia – Dori

Odpowiedz

4

Zamiast czek run-time dla null pointer, można użyć czek kompilacji bez obiektu:

Container() : comparison_object(new Default<T,T>), uses_default(true) {} 
Container(Compare<T,T>& cmp) : comparison_object(&cmp), uses_default(false) {} 

Domyślny konstruktor, a zatem Default zostanie instancja tylko w razie potrzeby, więc nie ma błędu podczas używania konstruktora innego niż domyślny z typem, dla którego nie powiodłoby się Default.

Ale bądź ostrożny żonglując surowymi wskazówkami, to przepis na przecieki pamięci i jeszcze gorzej. Nie zapomnij o wirtualnym destruktorze dla Compare i Rule of Three i zachowaj ostrożność, aby nieoczekiwany komparator nie został niespodziewanie zniszczony. Lepiej, użyj inteligentnego wskaźnika, aby zająć się tym wszystkim.

+0

Ale nadal muszę sprawdzić NULL w drugim konstruktorze, aw razie potrzeby utworzyć 'Default ' ...z powrotem do kwadratu – Dori

+0

@Dori: Rzeczywiście, zapomniałem wspomnieć, że powinieneś wziąć odniesienie, a nie wskaźnik, aby upewnić się, że nie może być zerowa. Jeśli chcesz domyślny komparator, użyj domyślnego konstruktora. –

+0

Nie. Podczas gdy kompilator odczytuje tę linię, utworzy instancję 'Default ', czyż nie? – ikh

0
template<typename T1, typename T2 > 
class Container { 
public: 
    template<typename T3, typename T4 > 
    class Compare { 
    public: 
     virtual bool eq(const T1&, const T2&) const = 0; 
    }; 


    class Default : public Compare { 
    public: 
     bool eq(const T1& a, const T2& b) const { return a==b; } 
    }; 

private: 
    Compare<T1,T2>* comparison_object; 
    bool uses_default; 
    Container(Compare<T1,T2>* cmp) : comparison_object(cmp), uses_default(false) { 
     if (!cmp) { 
      comparison_object = new Default<T,T>(); 
      uses_default = true; 
     } 
    } 
    ~Container() { if(uses_default) delete comparison_object; } 
};