2012-06-25 11 views
5

Byłem refactoring niektóre kod i stwierdził, że istnieją dwa miejsca, które można zapisać z tym samym kodem, z wyjątkiem komparator zestawu jest less<double> w jednym miejscu i greater<double> w drugim. Coś jak:std :: set wybierz mniej lub więcej komparatora w czasie wykonywania

double MyClass::Function1(double val) 
{ 
    std::set<double, less<double> > s; 
    // Do something with s 
} 

double MyClass::Function2(double val) 
{ 
    std::set<double, greater<double> > s; 
    // Do the same thing with s as in Function1 
} 

Pomyślałem więc robić:

double MyClass::GeneralFunction(double val, bool condition) 
{ 
    if(condition) 
    { 
     // Select greater as comparator 
    } 
    else 
    { 
     // Select less as comparator 
    } 

    set<double, comparator> s; 
    // common code 
} 

Zrobiłem to działa przy użyciu moje własne funkcje porównawcze, tak:

bool my_greater(double lhs, double rhs) 
{ 
    return lhs > rhs; 
} 

bool my_less(double lhs, double rhs) 
{ 
    return lhs < rhs; 
} 

double MyClass::GeneralFunction(double val, bool condition) 
{ 
    typedef bool(*Comparator) (double, double); 
    Comparator comp = &my_less; 
    if (condition) 
    { 
     comp = &my_greater; 
    } 

    std::set<double, Comparator > s(comp); 

    //.... 
} 

Ale chciałbym używać wbudowanych. Problem polega na tym, że nie wiem, jak zadeklarować komparatora i przypisać mu wbudowane predykaty.

Każda pomoc zostanie bardzo doceniona.

Odpowiedz

4

Czy naprawdę potrzebujesz kontrolę wykonywania?

template <class Comp> double MyClass::Function(double val) 
{ 
    std::set<double, Comp > s; 
    // Do something with s 
} 

Nawet jeśli nie, można nadal korzystać

double MyClass::Function(double val, bool comp) 
{ 
    return comp ? Function<std::less<double> >(val) : Function<std::greater<double> >(val); 
} 
+0

Dzięki! Naprawdę nie potrzebowałem sprawdzania w czasie wykonywania, więc mogłem wybrać komparator w programie wywołującym. – MikMik

2

Dlaczego nie

template <typename Compare> 
double MyClass::GeneralFunction(double val) 
{ 
    std::set<double, Compare> s; 

    //.... 
} 

wybór szablonu przez parametru formalnego nie jest czymś, C++ obsługuje bardzo dobrze. Przesyłaj jak najwięcej do fazy kompilacji, podając dostawcy wywołania argument szablonu.

Następnie można dostarczyć opakowanie, jeśli naprawdę chcesz, aby wybrać jeden w czasie wykonywania:

double MyClass::GeneralFunction(double val, bool condition) 
{ 
    return condition ? 
     GeneralFunction<std::greater<double> >(val) : 
     GeneralFunction<std::less <double> >(val);\ 
} 
3

Wystarczy użyć

std::set<double, std::function<bool(double,double)>> 

jako zestaw i oznacz ją tak:

typedef std::set<double, std::function<bool(double,double)> > RTSet; 

RTSet choose_ordering(bool increasing) 
{ 
    if (increasing) 
     return RTSet(std::less<double>()); 
    else 
     return RTSet(std::greater<double>()); 
} 

Uwaga w ogólnym swojej kompromis jest albo:

  • check zamawianie na każdym stosunku, lub
  • sprawdzić to raz na konkretyzacji ale potem również pobierana jest zadnie na każdym wywołaniu funkcji (takich jak wirtualne wywołanie funkcji dla przykładem)

ja preferują drugą opcję, więc nie można przypadkowo zmienić kolejność, a zestaw jest używany, łamiąc wszystkie jego niezmienniki.


Tylko szybka myśl, jak to może być potencjalnie osobną odpowiedź (a nawet pytanie), ale można wymienić dwa fragmenty kodu są identyczne z wyjątkiem porządku sortowania.

Alternatywą Użyłem w niektórych sytuacjach jest zastosowanie pojedynczego kierunek sortowania i szablon kodu operacyjny na planie (wg rodzaju iteracyjnej), więc można zrobić

if (increasing) 
    do_stuff(set.begin(), set.end()); 
else 
    do_stuff(set.rbegin(), set.rend()); 
+0

+1 do używania 'std :: function'. W odniesieniu do ryzyka przypadkowej zmiany kolejności, gdy zestaw jest w użyciu: jakakolwiek flaga powinna być prywatnym elementem obiektu funkcjonalnego, co znacznie zmniejsza ryzyko. –

+0

Część std :: function była tym, o co właściwie prosiłem, ale odpowiedź MSaltersa była lepszym rozwiązaniem, jak sądzę. – MikMik

4

Problem polega na tym, że nie można wybrać modelu komparatora w trybie , a std::less i std::greater mają niepowiązane typy. Podobnie, std::set z instancją std::less jako kompilatorem ma typ niezwiązany z instancją z std::greater. Istnieje kilka możliwych rozwiązań, ale najprostszy (i tylko jeden nie udziałem inhertance, funkcje wirtualne i dynamicznej alokacji) jest wzdłuż linie, co robisz:

class SelectableCompare 
{ 
    bool myIsGreater; 
public: 
    SelectableCompare(bool isGreater) : myIsGreater(isGreater) {} 
    bool operator()(double d1, double d2) const 
    { 
     static std::less<double> const less; 
     return myIsGreater 
      ? less(d2, d1) 
      : less(d1, d2); 
    } 
}; 

Użyłem standardu std::less i std::greater, ponieważ wyraziłeś zainteresowanie tym, co robisz, pod numerem . W przypadku double, to jest, szczerze mówiąc, overkill; Normalnie po prostu napiszę d1 > d2 i d1 < d2. Jednak może to mieć sens, ponieważ niektóre typy mogą mieć wyspecjalizowaną odmianę std::less. Z tego powodu używam tylko std::less; można sobie wyobrazić, że programista specjalizuje się tylko w: std::less, wiedząc, że jest to jedyny program używany do porządkowania w standardowej bibliotece.

Wystarczy być kompletna: oczywistą alternatywą jest zastosowanie strategii wzór w porównawczym, z abstrakcyjnego bazy porównawczej:

class Comparator 
{ 
public: 
    virtual ~Comparator() {} 
    virtual bool isLessThan(double d1, double d2) const = 0; 
}; 

, to raczej oczywiste klasach pochodnych dla różnych porównań i wrapper zarządzać pamięci:

class ComparatorWrapper 
{ 
    std::shared_ptr<Comparator> myComparator; 
public: 
    ComparatorWrapper(Comparator* newed_comparator) 
     : myComparator(newed_comparator) 
    { 
    } 
    bool operator()(double d1, double d2) const 
    { 
     return myComparator->isLessThan(d1, d2); 
    } 
}; 

jest to zdecydowanie przesada dla binarnego wyboru trzeba, ale może być właściwe, gdyby było więcej możliwości wyboru; na przykład a set, który może być posortowany na jednym z wielu różnych pól (wszystkich różnych typów).