Ошибка "passing const xxx as 'this' argument discards qualifiers" в C++: как исправить
error: passing 'const MyClass' as 'this' argument of 'bool MyClass::operator<(const MyClass&)' discards qualifiers [-fpermissive]
Вот мой код:
#include <iostream>
#include <set>
class MyClass {
public:
MyClass(int val) : value(val) {}
bool operator<(const MyClass& other) {
return this->value < other.value;
}
int getValue() const { return value; }
private:
int value;
};
int main() {
std::set<MyClass> mySet;
mySet.insert(MyClass(10));
mySet.insert(MyClass(5));
for (const auto& elem : mySet) {
std::cout << elem.getValue() << " ";
}
return 0;
}
Ошибка возникает в строке с определением оператора < . Я слышал что-то про "const correctness", но не понимаю, как это связано. Почему компилятор говорит, что я отбрасываю квалификаторы? И как это исправить? Помогите, пожалуйста, разобраться. Заранее спасибо!
<<Причина ошибки: const-корректность и работа std::set
Ошибка возникает из-за того, что std::set хранит элементы как константные объекты. Когда вы вставляете элемент в set, контейнер должен сравнивать его с существующими элементами, чтобы определить правильную позицию. Для этого он вызывает оператор сравнения, передавая константные ссылки на элементы. Однако ваш оператор operator< объявлен как неконстантный метод, то есть он может изменять объект, для которого вызывается (this не является константным). Компилятор запрещает вызывать неконстантные методы для константных объектов, отсюда и ошибка "discards qualifiers".
Решение: сделайте оператор сравнения константным
Исправление очень простое — добавьте ключевое слово const после объявления оператора:
bool operator<(const MyClass& other) const {
return this->value < other.value;
}
Теперь метод обещает не изменять объект, для которого вызывается (this становится const MyClass*), и его можно безопасно вызывать для константных объектов.
Полный исправленный код
#include <iostream>
#include <set>
class MyClass {
public:
MyClass(int val) : value(val) {}
// Важно: добавляем const
bool operator<(const MyClass& other) const {
return this->value < other.value;
}
int getValue() const { return value; }
private:
int value;
};
int main() {
std::set<MyClass> mySet;
mySet.insert(MyClass(10));
mySet.insert(MyClass(5));
for (const auto& elem : mySet) {
std::cout << elem.getValue() << " ";
}
// Вывод: 5 10 (отсортировано)
return 0;
}
Почему это важно? Правило const-корректности
В C++ действует важное правило: если метод не изменяет состояние объекта, он должен быть объявлен как const. Это:
- Повышает безопасность — компилятор проверит, что вы случайно не измените объект в методе, который не должен этого делать.
- Позволяет вызывать метод для константных объектов — как в нашем случае с std::set.
- Улучшает читаемость кода — сразу видно, какие методы являются "наблюдателями" (не меняют объект), а какие — "модификаторами".
Дополнительные рекомендации
1. Всегда добавляйте const к операторам сравнения (operator<, operator== и т.д.), если они не должны изменять объект.
2. Для std::set и std::map оператор сравнения обязательно должен быть константным, иначе компиляция не удастся.
3. Используйте const для методов-геттеров — как вы уже сделали с getValue() const.
4. Если вы используете C++20, можно автоматически сгенерировать оператор сравнения с помощью auto operator<=>(const MyClass&) const = default; (космический корабль).
В итоге: ошибка "passing const xxx as 'this' argument discards qualifiers" почти всегда решается добавлением const к методу, который вызывается для константного объекта. В вашем случае — к оператору сравнения.
Альтернативный подход: использование внешней функции сравнения
Вместо перегрузки оператора operator< внутри класса, вы можете определить внешнюю функцию сравнения и передать её в std::set как компаратор. Это может быть полезно, если вы не хотите изменять класс или если вам нужно несколько разных способов сравнения.
#include <iostream>
#include <set>
class MyClass {
public:
MyClass(int val) : value(val) {}
int getValue() const { return value; }
private:
int value;
};
// Внешняя функция сравнения
struct CompareMyClass {
bool operator()(const MyClass& a, const MyClass& b) const {
return a.getValue() < b.getValue();
}
};
int main() {
// Передаём компаратор как второй шаблонный параметр
std::set<MyClass, CompareMyClass> mySet;
mySet.insert(MyClass(10));
mySet.insert(MyClass(5));
for (const auto& elem : mySet) {
std::cout << elem.getValue() << " ";
}
// Вывод: 5 10
return 0;
}
Когда это может быть полезно?
- Если класс из сторонней библиотеки — вы не можете изменить его код, но можете определить свой компаратор.
- Если нужно несколько критериев сортировки — создайте разные компараторы для разных контейнеров.
- Если сравнение требует доступа к приватным данным — объявите компаратор как
friendкласса.
Этот подход полностью решает проблему с const-корректностью, так как внешняя функция принимает константные ссылки и сама объявлена как const. Однако он более многословен, чем добавление const к оператору внутри класса. Выбирайте тот способ, который лучше подходит для вашей ситуации.
Ответов пока нет.
Войдите чтобы оставить ответ