Передайте пользовательский компаратор через функцию

У меня есть класс с функцией

MyClass::doStuff(std::vector<MyCustomData*> toSort) { ...

в котором я звоню

std::sort(toSort.begin(), toSort.end(), MyClass::SortByZ());

myClass::SortByZ() — это настраиваемый компаратор. Теперь это работает, но я хотел бы добиться следующего:

У меня есть несколько классов, каждый из которых должен иметь свой собственный функтор компаратора для сортировки "MyCustomData". Так, например. Class1... должен иметь

class Class1 {
    struct SortData {
        bool operator ()(MyCustomData *lhs, MyCustomData *rhs) {
        return lhs->something1 > rhs->something1;
        }
    };
    //...many more functions/vars
}

в то время как Class2 имеет другой функтор компаратора для одного и того же типа данных, например

class Class2 {
    struct SortData {
        bool operator ()(MyCustomData *lhs, MyCustomData *rhs) {
        return lhs->something2 > rhs->something2;
        }
    };
    //...many more functions/vars
}

Теперь я хотел бы иметь возможность вызывать функцию MyClass::doStuff(...) либо с

doStuff(myData, Class1::SortData)

or

doStuff(myData, Class2::SortData)

и функция MyClass::doStuff(...) должна использовать соответствующий Sort-Order.

Я не нашел способ сделать это, есть ли он? Мне нужно простое решение (не нужно поддерживать шаблоны или что-то в этом роде). Я был бы готов использовать boost, если бы мне это было нужно, но решение без boost было бы предпочтительнее.

Надеюсь, я смог описать, чего я хочу достичь? Спасибо за любую помощь!


person Ela782    schedule 20.04.2012    source источник
comment
Если ваш vector хранит MyCustomData объекты, а не указатели, ваши SortData функторы должны иметь другую подпись: bool operator()(MyCustomData const & lhs, MyCustomData const & rhs).   -  person Luc Touraille    schedule 20.04.2012
comment
Извините, я был неясен об этом. Вектор хранит указатели, поэтому vector‹MyCustomData*›, поэтому сигнатура функтора работает.   -  person Ela782    schedule 20.04.2012


Ответы (2)


Вам нужно будет сделать doStuff шаблон:

template <typename Comparator>
void doStuff(std::vector<MyCustomData*> toSort, Comparator compare) {
   // ...
   std::sort(toSort.begin(), toSort.end(), compare);
   // ...
}

Кроме того, он может захотеть взять первый аргумент по ссылке. Как бы то ни было, он сортирует копию аргумента, отбрасывает эту копию и оставляет нетронутым вектор вызывающего объекта; хотя, возможно, это то, что вы хотите.

person Mike Seymour    schedule 20.04.2012
comment
Спасибо за ответ, а также Люку! Я только что нашел еще две идеи, которые могут сработать и помочь мне. Можно ли вызвать функцию/функтор сравнения с аргументом (строкой)? Затем я бы вызвал doStuff(myData, SortByThis), и компаратор мог бы сделать bool operator (...string sortByThis?...)(MyCustomData const & lhs, MyCustomData const & rhs) { ...return lhs.something.find[sortByThis]->second > rhs.something.find[sortByThis]->second; Потому что я сортирую по некоторым записям в этом std::map что-то. - person Ela782; 20.04.2012
comment
Вторая идея заключалась бы в том, чтобы дать структуре переменную string SortByThis и использовать ее в операторе () или сделать структуру классом, создать ее экземпляр, установить SortByThis в нужную строку и использовать ее? Думаю попробовать все три. Есть ли что-то лучше другого с точки зрения разработки программного обеспечения? - person Ela782; 20.04.2012
comment
@ Ela782: Второй способ - это то, как вы это сделаете. Вы не можете изменить оператор вызова функции компаратора; это должно быть что-то вроде bool operator()(T a, T b); без дополнительных аргументов. Но вы можете поместить элементы данных в компаратор и использовать их в операторе. - person Mike Seymour; 20.04.2012
comment
Итак, я закончил тем, что сделал class SortData { public: SortData (std::string sortType) { this->sortType= sortType; }; bool operator ()(MyCustomData *lhs, MyCustomData *rhs) { return lhs->something.find(sortType)->second > rhs->something.find(sortType)->second; }; private: std::string sortType; }; и вызвал его с помощью std::sort(toSort.begin(), toSort.end(), MyCustomData::SortData("mySortOrder")); Я думаю, это лучшее решение, чем использование шаблонов? Но ваш совет по шаблону был столь же полезен, большое спасибо! - person Ela782; 20.04.2012

Используйте шаблон функции, чтобы принять любую функцию сравнения (или функтор):

template <typename Comparator>
void doStuff(std::vector<MyCustomData> toSort, Comparator comparator)
{
    ...
    std::sort(toSort.begin(), toSort.end(), comparator);
    ...
}
...
doStuff(myData, Class1::SortData());
doStuff(myData, Class2::SortData());

Вот как стандартные алгоритмы обеспечивают универсальность.

person Luc Touraille    schedule 20.04.2012
comment
Жаль, что я могу принять только один ответ, так как ваш такой же и одинаково полезный. И вы, вероятно, были медленнее только потому, что вы сначала ввели комментарий о моей неправильной подписи функтора. Так что я не знаю, кого я должен выбрать, я очень новичок здесь. Я очень благодарна вам за вашу помощь! - person Ela782; 20.04.2012
comment
@ Ela782: Не волнуйся, я не пропущу 15 очков репутации :). Обычно, когда на вопрос есть несколько одинаковых ответов, принято принимать тот, который пришел первым. В этом случае ответ @Mike даже немного лучше, поскольку он обращает внимание на тот факт, что toSort передается по значению. Я подумал об удалении своего ответа, но оставлю его, поскольку в нем приведен пример использования шаблона функции. - person Luc Touraille; 20.04.2012