Компаратор для множества, определенного в классе элементов множества

У меня есть класс, и указатели на объекты этого класса нужно поместить в std::set. Я хочу определить компаратор внутри класса. Я видел несколько решений, в которых либо определяется отдельный класс (наверное, он называется функтором), либо определяется структура, которая перегружает класс operator(). Я хочу избежать этого шаблонного кода и хочу определить компаратор как член самого класса, что-то вроде метода compareTo() в Java.

Скажем, мой класс выглядит примерно так:

class Item {
private:
    int id;
    float score;
.....
public:
// Rest of the methods and setters/getters
}

Я хочу определить компаратор таким образом, чтобы указатель на объект с более высокой оценкой помещался первым в наборе. Если количество очков у обоих одинаковое, то первым ставится тот, у кого id меньше. Я предполагаю, что код будет примерно таким, но, поскольку я не очень хорошо понял эту часть, пожалуйста, поправьте меня (я бы хотел, чтобы это было размещено внутри самого класса):

bool operator()(const Item* a, const Item* b) {
    if (a->score != b->score) return a->score > b->score;
    return a->id < b->id;
}

Использование будет следующим:

std::set<Item*> sortedItems;
Item* item = new Item();
sortedItems.insert(item);

Я не уверен, нужно ли вообще указывать компаратор в шаблоне std::set, если он определен в классе, и если да, то как? Кроме того, как добавить этот компаратор в сам класс? Я новичок в STL и довольно новичок в C++. Спасибо!


person Paresh    schedule 11.10.2012    source источник
comment
Вам нужно будет определить operator< для элемента.   -  person andre    schedule 11.10.2012
comment
@ahenderson Понятно. И аргументами для этого будут указатели на Item или сам Item? Кроме того, если operator< определено, мне не нужно будет указывать какой-либо компаратор для std::set? Я использую set указателей на Item, а не сами объекты. Спасибо за ваш ответ!   -  person Paresh    schedule 11.10.2012
comment
извините, я не заметил, что у вас есть набор указателей   -  person andre    schedule 11.10.2012
comment
Довольно просто написать шаблонный ptrOrder<T>, который заказывает два T*, используя std::less<T>, который, в свою очередь, по умолчанию равен operator<(T, T). Это может быть полезно, если у вашего Item уже есть работающие operator< и std::less<Item>.   -  person MSalters    schedule 11.10.2012
comment
@MSalters Я вижу. Спасибо! Но поскольку в классе Item их нет, достаточно будет и простого написания компаратора.   -  person Paresh    schedule 11.10.2012
comment
Использование указателей и голых new очень не соответствует C++, потому что обработка времени жизни подвержена ошибкам. Современный стиль C++ заключается в использовании интеллектуальных указателей или каких-либо дескрипторов. И вы можете перегрузить operator< для них, потому что они являются типами классов.   -  person Jan Hudec    schedule 11.10.2012
comment
@JanHudec: Нет, не совсем. Вы не можете пойти и перегрузить operator< ради std::shared_ptr. В каком пространстве имен вы бы это сделали? Помните, что std::set должен его найти, и вы не можете добавлять перегрузки к namespace std.   -  person MSalters    schedule 12.10.2012
comment
@MSalters: Конечно, в пространстве имен T. 3.4.2/2 говорится, что связанные пространства имен экземпляра шаблона класса включают в себя связанные пространства имен его параметров шаблона типа.   -  person Jan Hudec    schedule 12.10.2012


Ответы (2)


это решение основано на этом ответе.

#include <set>

class Item {
private:
    int id;
    float score;
public:
    struct compare {
        bool operator()(const Item* a, const Item* b) {
             if (a->score != b->score) return a->score > b->score;
             return a->id < b->id;
        }
    };
};

Поскольку set позволяет вам определить собственный метод сравнения, вы можете использовать его следующим образом.

std::set<Item*, Item::compare> sortedItems;

Это должно позволить вашему классу Item работать с набором

person andre    schedule 11.10.2012
comment
Ага. Это отлично работает! Благодарю вас! Я надеялся на что-то без объявления структуры, но я думаю, что это лучший способ. Тем не менее, структура как член класса намного чище, чем отдельная структура или класс. Еще раз, спасибо! - person Paresh; 11.10.2012
comment
Спасибо, именно то, что я искал! Но что произойдет, если вы измените оценку одного из уже вставленных участников? Я думаю, вам придется стереть и вставить его снова, потому что сам набор не может проверить, изменился ли один из его членов, верно? - person v01pe; 28.05.2013
comment
@ v01pe Если вы планируете внести изменения в набор, я бы удалил его, сначала внесите изменение, а затем снова вставьте его. - person andre; 28.05.2013

Реализация set<T> хочет вызвать a < b, где a и b — это объекты типа T. Пока этот вызов действителен, набору все равно; это может быть нестатическая функция-член, принимающая один аргумент, статическая функция-член, принимающая два аргумента, или свободная функция, принимающая два аргумента:

class Item {
public:
    bool operator<(const Item& rhs) {
        return score == rhs.score ? id < rhs.id : score < rhs.score;
    }
static bool operator<(const Iterm& lhs, const Item& rhs) {
    return lhs.score == rhs.score ? lhs.id < rhs.id : lhs.score < rhs.score;
}
};

bool operator<(const Item& lhs, const Item& rhs) {
    return lhs.score == rhs.score ? lhs.id < rhs.id : lhs.score < rhs.score;
}

Любой из этих трех в порядке. Конечно, если вы напишете два или более из них, вы получите двусмысленность.

person Pete Becker    schedule 11.10.2012
comment
Я не думаю, что std::set будет вызывать какие-либо методы при сравнении указателей. - person andre; 11.10.2012
comment
Спасибо! Обратите внимание, что набор будет иметь тип set<Item*>, тогда как переопределение operator< определено для Item. Похоже, в данном случае это не работает (множество сортируется по порядку вставки, что, я полагаю, потому что сравниваются указатели). - person Paresh; 11.10.2012
comment
@ахендерсон - правильно. Я не заметил, что вопрос задавался набором указателей. Тем не менее, мой ответ правильный ‹g›, потому что он говорит о set<T>. - person Pete Becker; 11.10.2012
comment
@Paresh - для указателей вы не можете написать перегруженный operator<, потому что язык уже определил его для вас. Так что сделайте, как предложил ахендерсон: напишите отдельную функцию сравнения и передайте ее в сортировку. - person Pete Becker; 11.10.2012