Почему const необходим для "operator>", но не для "operator <"?

Рассмотрим этот фрагмент кода:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

struct MyStruct
{
    int key;
    std::string stringValue;

    MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}

    bool operator < (const MyStruct& other) {
        return (key < other.key);
    }
};

int main() {
    std::vector < MyStruct > vec;

    vec.push_back(MyStruct(2, "is"));
    vec.push_back(MyStruct(1, "this"));
    vec.push_back(MyStruct(4, "test"));
    vec.push_back(MyStruct(3, "a"));

    std::sort(vec.begin(), vec.end());

    for (const MyStruct& a : vec) {
        cout << a.key << ": " << a.stringValue << endl;
    }
}

Он компилируется отлично и дает результат, которого можно было бы ожидать. Но если я попытаюсь отсортировать структуры в порядке убывания:

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

struct MyStruct
{
    int key;
    std::string stringValue;

    MyStruct(int k, const std::string& s) : key(k), stringValue(s) {}

    bool operator > (const MyStruct& other) {
        return (key > other.key);
    }
};


int main() {
    std::vector < MyStruct > vec;

    vec.push_back(MyStruct(2, "is"));
    vec.push_back(MyStruct(1, "this"));
    vec.push_back(MyStruct(4, "test"));
    vec.push_back(MyStruct(3, "a"));

    std::sort(vec.begin(), vec.end(), greater<MyStruct>());

    for (const MyStruct& a : vec) {
        cout << a.key << ": " << a.stringValue << endl;
    }
}

Это дает мне ошибку. Вот полное сообщение:

/usr/include/c++/7.2.0/bits/stl_function.h: В экземпляре 'constexpr bool std :: больше <_Tp> :: operator() (const _Tp &, const _Tp &) const [с _Tp = MyStruct ]:
/usr/include/c++/7.2.0/bits/stl_function.h:376:20: ошибка: нет соответствия для 'operator>' (типы операндов: 'const MyStruct' и 'const MyStruct')
{return __x> __y; } }

Похоже, что эта функция прямо здесь не имеет определителя const:

bool operator > (const MyStruct& other) {
        return (key > other.key);
}

Если я добавлю его,

bool operator > (const MyStruct& other) const {
        return (key > other.key);
}

Тогда все хорошо. Почему это так? Я не слишком хорошо знаком с перегрузкой оператора, поэтому я просто поместил его в память, что нам нужно добавить const но все же странно, почему она работает для operator< без const.

Ответ 1

Вы получаете разные формы поведения, потому что на самом деле вы вызываете две разные (перегруженные) функции сортировки.

В первом случае вы вызываете два параметра std::sort, которые используют operator< непосредственно. Поскольку итераторы к вашим векторным элементам создают неконстантные ссылки, он может применять operator< just fine.

Во втором случае вы используете трехпараметрическую версию std::sort. Тот, который принимает функтор. Вы проходите std::greater. И этот функтор имеет operator() объявленный следующим образом:

constexpr bool operator()( const T& lhs, const T& rhs ) const;

Обратите внимание на ссылки const. Он связывает элементы, которые необходимо сравнить с ссылками на const. Поэтому ваш собственный operator> должен быть также корректным.

Если вы должны были вызвать std::sort с std::less, ваш operator< будет производить ту же ошибку, потому что он не const-correct.

Ответ 2

Использование std::sort(vec.begin(), vec.end()) зависит только от operator< function. Это не требует, чтобы функция могла работать с объектами const.

std::greater, с другой стороны, требует, чтобы функция могла работать с объектами const.

Аналогичная проблема вы увидите, если вы используете std::less, например std::sort(vec.begin(), vec.end(), std::less<MyStruct>()).


Сказав это, нет никаких причин, по которым operator< функция и operator> функциями non- const. Любая функция член, не изменяет данные члены должны быть выполнены в const функции члена.