Перегрузка как оператора <и операторa> в том же классе

В моей домашней работе мне нужно создать класс Message; среди других атрибутов он имеет атрибут "приоритет" (основная цель - реализовать очередь приоритетов).

Как и в контейнере, я должен проверить, больше ли один объект, чем другой, я перегрузил оператор ' > '. Теперь у меня есть несколько общих вопросов об этом...

Вопрос один:

Если я перегружаю оператор ' > ', я должен перегрузить оператор '<' для аргументов (const Message &, const Message &)?

Мое мнение таково, что перегрузка и > и < и использование его в коде вызовет ошибку:

if(message1 > message2)
   { ... }

(Является ли следующий код вызовом operator > для объекта message1 или объекта operator < message2?)

Но что, если я использую оператор следующим образом:

if(message1 < message2)
   { ... }

?

Оператор

объявляется как функция друга:

friend bool operator>(const Message& m1, const Message& m2)

Нужно ли его объявлять как функцию-член?

Спасибо.

Ответ 1

Если я перегружаю оператор ' > ', я должен перегрузить оператор '<' для argumenst (const Message &, const Message &)?

Да. Фактически, его соглашение в большинстве кодов предпочитает использовать < над > (не спрашивайте меня, почему, вероятно, исторический). Но в целом, всегда перегружайте полный набор связанных операторов; в вашем случае это, вероятно, также будет ==, !=, <= и >=.

(Является ли следующий код вызовом operator > для объекта message1 или объекта operator < message2?)

Он всегда вызывает то, что находит в коде. Для компилятора С++ нет абсолютно никакой связи между > и <. Для нас они похожи, но компилятор видит два совершенно разных, несвязанных символа. Поэтому нет никакой двусмысленности: компилятор называет то, что видит.

Нужно ли его объявлять как функцию-член?

Нет. Фактически, его лучше не объявлять как функцию-член. Объявление его как функции-члена означает, что первый аргумент (т.е. Левая часть выражения) должен действительно быть объектом Message, а не объектом, который неявно конвертируется в Message.

Чтобы понять это, рассмотрим следующий случай:

struct RealFraction {
    RealFraction(int x) { this.num = x; this.den = 1; }
    RealFraction(int num, int den) { normalize(num, den); }
    // Rest of code omitted.

    bool operator <(RealFraction const& rhs) {
        return num * rhs.den < den * rhs.num;
    }
};

Теперь вы можете написать следующее сравнение:

int x = 1;
RealFraction y = 2;
if (y < x) …

но вы не можете написать следующее:

if (x < y) …

хотя существует неявное преобразование из int в RealFraction (с использованием первого конструктора).

Если, с другой стороны, вы использовали функцию, не являющуюся членом для реализации оператора, оба сравнения будут работать, потому что С++ знал бы, чтобы вызвать неявный конструктор по первому аргументу.

Ответ 2

Да, вы должны... но вы можете (и, возможно, должны) реализовать три из <, >, <=, >= в терминах другой. Это гарантирует, что они будут вести себя последовательно. Обычно < - это тот, который другие реализованы в терминах, поскольку он используется по умолчанию для операторов set и map s.

например. если вы внедрили <, вы можете определить >, <= и >= следующим образом.

inline bool operator>(const Message& lhs, const Message& rhs)
{
    return rhs < lhs;
}

inline bool operator<=(const Message& lhs, const Message& rhs)
{
    return !(rhs < lhs);
}

inline bool operator>=(const Message& lhs, const Message& rhs)
{
    return !(lhs < rhs);
}

== и != часто реализуются отдельно. Иногда классы реализуют == такие, что a == b тогда и только тогда, когда !(a < b) && !(b < a), но иногда == реализуется как более строгая связь, чем !(a < b) && !(b < a). Однако это приводит к большей сложности для клиента этого класса.

В некоторых ситуациях допустимо иметь <, >, <= и >=, но не == или !=.

Ответ 3

Если присваивание явно не требует использования перегрузки оператора, вы также можете рассмотреть использование объекта функции. Причина в том, что существует, вероятно, более одного способа сравнить два Сообщения для "меньше" (например, сравнить содержимое лексикографически, время публикации и т.д.), И поэтому значение operator< не является интуитивно понятным.

С std::priority_queue используемый объект функции указывается в качестве третьего параметра шаблона (к сожалению, вам также необходимо указать второй тип контейнера):

#include <queue>
#include <string>
#include <functional>
#include <vector>

class Message
{
    int priority;
    std::string contents;
    //...
public:
    Message(int priority, const std::string msg):
        priority(priority),
        contents(msg)
    {}
    int get_priority() const { return priority; }
    //...
};

struct ComparePriority:
    std::binary_function<Message, Message, bool> //this is just to be nice
{
    bool operator()(const Message& a, const Message& b) const
    {
        return a.get_priority() < b.get_priority();
    }
};

int main()
{
    typedef std::priority_queue<Message, std::vector<Message>, ComparePriority> MessageQueue;
    MessageQueue my_messages;
    my_messages.push(Message(10, "Come at once"));
}

При реализации собственной очереди приоритетов вы можете использовать ее следующим образом:

class MessageQueue
{
    std::vector<Message> messages;
    ComparePriority compare;
    //...
    void push(const Message& msg)
    {
        //...
        if (compare(msg, messages[x])) //msg has lower priority
        //...
    }
};