Проблема с перегрузкой оператора ‹ в C++

У меня есть вектор объектов Student, которые я хочу отсортировать, используя #include <algorithm> и sort(list.begin(), list.end());

Я понимаю, что для этого мне нужно перегрузить оператор «‹», но после попытки (и неудачи) нескольких методов, предложенных в Интернете, у меня заканчиваются идеи.

Вот моя последняя попытка:

В Студент.ч...

...
using namespace std;
class Student
{
    friend bool operator <(const Student& first, const Student& second);
    public:
    ...
    private:
    ...
};

А в Student.cpp...

...
#include "Student.h"
using namespace std;
...
bool operator <(const Student& first, const Student& second)
{
    return first.Name() < second.Name();
}

где «Name()» — это константная функция, возвращающая строку.

Программа компилируется и запускается, но моя функция оператора никогда не вызывается во время сортировки, и когда я попытался сравнить два объекта Student, таких как s1 < s2, я получил «ошибка: перегруженный оператор не найден»

Как правильно перегрузить этот оператор, чтобы моя сортировка работала так, как я задумал?


person Paul Williams    schedule 12.10.2009    source источник


Ответы (4)


Я бы не стал использовать друга здесь, и я не уверен, что это вообще работает. То, что я бы использовал, это...

class Student
{
  public:
    bool operator< (const Student& second) const;
};

bool Student::operator< (const Student& second) const
{
  return (Name() < second.Name());
}

Обратите внимание на константу в конце, указывающую, что внутри оператора‹ *это константа.

ИЗМЕНИТЬ Я не могу удалить этот ответ, потому что он был принят, но я бы сделал это, если бы мог. Я также не могу заменить его на правильный. См. ниже комментарий Дрю Дорманна и ответ Росса Смита.

person Steve314    schedule 12.10.2009
comment
Я думаю, что, наконец, решил, что это ответ, который я выберу, потому что это тот, который наиболее непосредственно привел меня к выявлению большей проблемы, которая у меня была с моими указателями. - person Paul Williams; 12.10.2009
comment
Этого подхода часто избегают, потому что, если типы могут быть преобразованы в Student, код будет преобразовывать правосторонний тип, но не левосторонний. Функция друга будет вести себя последовательно для обеих сторон. - person Drew Dormann; 12.10.2009

Вы не сказали, какой компилятор вы использовали, но я подозреваю, что вы используете довольно новый, который реализует правило «друг не является объявлением». Оператор друга внутри класса не служит объявлением функции; другие модули, включающие Student.h, не видят объявления функции. Он виден только внутри файла Student.cpp. (Старые компиляторы не имеют этого правила и обрабатывают объявление друга как объявление функции.)

Функция не обязана быть другом, так как она не использует каких-либо закрытых членов класса Student (я предполагаю, что Name() является общедоступным). Переместите объявление функции за пределы класса и замените «друг» на «внешний», и все должно работать.

Можно сделать оператор функцией-членом, как предлагалось выше, но это не обязательно. Создание функций-членов операторов сравнения обычно не одобряется, потому что это означает, что два аргумента не обрабатываются симметрично (один является невидимым аргументом «this», другой — обычным аргументом функции), что в некоторых случаях может привести к неожиданным результатам. (например, к аргументам могут применяться различные преобразования типов).

person Ross Smith    schedule 12.10.2009
comment
Нужно ли помечать объявление функции extern? Я делал это только с переменными, объявленными в заголовочных файлах. - person Anthony Cramp; 12.10.2009
comment
Трудно было определиться с одним ответом на этот вопрос. Все мои первоначальные попытки были определены в классе, и я попробовал версию для друзей только из отчаяния, прочитав чье-то заявление о том, что сортировка не будет выполнена, если вы этого не сделаете. Оказалось, что у меня действительно была проблема, связанная с указателем. - person Paul Williams; 12.10.2009
comment
Правда, в этом нет необходимости; функции вне классов имеют внешнюю связь по умолчанию. У меня есть привычка вставлять его, чтобы указать читателю, что функция определена в другом исходном файле (в отличие от прямого объявления функции, определенной в другом месте в том же файле). - person Ross Smith; 12.10.2009
comment
@Ross - я понимаю хмурость, но лично я хмурюсь на людей, которые написали стандарт. Логический симметричный подход состоит в том, чтобы иметь статическую функцию-член, но, по-видимому, это не разрешено. Как бы то ни было, мои привычки сформировались из-за проблем с подходом к друзьям - может быть, эти проблемы уже ушли, но мои нынешние привычки не вызывают у меня никаких проблем. ИМО, если это не сломано, не чините, это относится и к привычкам. - person Steve314; 12.10.2009

Кстати, с такой простой функцией лично я бы сделал так, если только руководство по стилю не запрещает определение функций внутри определений классов:

class Student
{
    friend bool operator <(const Student& first, const Student& second)
    {
        return first.Name() < second.Name();
    }
    ...
};

Эта форма объявления «друга» позволяет вам определить не члена operator< внутри тела класса. Это все еще друг, так что Name() может быть приватным, если хотите.

person Steve Jessop    schedule 12.10.2009

Ну, вы могли бы сделать это как внутренний оператор:

class Student
{
    public:
    bool operator <(const Student& second) const;
    ...
    private:
    ...
};

С реализацией, сравнивающей это со вторым. На мой взгляд, возникает вопрос: имеет ли метод «Имя» константный вкус? Потому что если это не так, то вы не сможете написать константный метод, который его использует.

person Michael Kohne    schedule 12.10.2009
comment
Кажется вероятным - и первый, и второй параметры в версии друга были константами. - person Steve314; 12.10.2009