Ошибка: передача xxx в качестве аргумента 'this' аргумента xxx отбрасывает квалификаторы

#include <iostream>
#include <set>

using namespace std;

class StudentT {

public:
    int id;
    string name;
public:
    StudentT(int _id, string _name) : id(_id), name(_name) {
    }
    int getId() {
        return id;
    }
    string getName() {
        return name;
    }
};

inline bool operator< (StudentT s1, StudentT s2) {
    return  s1.getId() < s2.getId();
}

int main() {

    set<StudentT> st;
    StudentT s1(0, "Tom");
    StudentT s2(1, "Tim");
    st.insert(s1);
    st.insert(s2);
    set<StudentT> :: iterator itr;
    for (itr = st.begin(); itr != st.end(); itr++) {
        cout << itr->getId() << " " << itr->getName() << endl;
    }
    return 0;
}

В строке:

cout << itr->getId() << " " << itr->getName() << endl;

Выдается сообщение об ошибке:

../main.cpp: 35: ошибка: передача 'const StudentT' в качестве аргумента 'this' аргумента 'int StudentT:: getId()' отбрасывает квалификаторы

../main.cpp: 35: ошибка: передача 'const StudentT' в качестве 'this' аргумента 'std::string StudentT:: getName()' отбрасывает квалификаторы

Что не так с этим кодом? Спасибо!

Ответ 1

Объекты в std::set сохраняются как const StudentT. Поэтому, когда вы пытаетесь вызвать getId() с объектом const, компилятор обнаруживает проблему, а именно вы вызываете неконстантную функцию-член в объекте const, которая не разрешена, потому что не-константные функции-члены не делают НИКАКИХ ОБЯЗАННОСТЕЙ, чтобы не изменить объект; поэтому компилятор должен сделать безопасное предположение, что getId() может попытаться изменить объект, но в то же время он также замечает, что объект const; поэтому любая попытка изменить объект const должна быть ошибкой. Следовательно, компилятор генерирует сообщение об ошибке.

Решение прост: создайте функции const как:

int getId() const {
    return id;
}
string getName() const {
    return name;
}

Это необходимо, потому что теперь вы можете вызывать getId() и getName() для объектов const как:

void f(const StudentT & s)
{
     cout << s.getId();   //now okay, but error with your versions
     cout << s.getName(); //now okay, but error with your versions
}

В качестве побочного элемента вы должны реализовать operator< как:

inline bool operator< (const StudentT & s1, const StudentT & s2)
{
    return  s1.getId() < s2.getId();
}

Параметры примечаний теперь const.

Ответ 2

Функции-члены, которые не изменяют экземпляр класса, должны быть объявлены как const:

int getId() const {
    return id;
}
string getName() const {
    return name;
}

В любое время вы видите "отбрасывает квалификаторы", это говорит о const или volatile.

Ответ 3

На самом деле стандарт С++ (т.е. С++ 0x draft) говорит (tnx to @Xeo и @Ben Voigt для указания на это мне):

23.2.4 Ассоциативные контейнеры
5 Для набора и мультимножества тип значения совпадает с типом ключа. Для карты и multimap он равен паре. Ключи в ассоциативной контейнер неизменен.
6 итератор ассоциативный контейнер двунаправленная категория итераторов. Для ассоциативные контейнеры, где значение тип совпадает с типом ключа, оба итератор и const_iterator постоянные итераторы. Он неуточнен будь то итератор или нет const_iterator - это один и тот же тип.

Итак, реализация Dinkumware VС++ 2008 ошибочна.


Старый ответ:

У вас есть эта ошибка, потому что в некоторых реализациях std lib set::iterator совпадает с set::const_iterator.

Например, libstdС++ (поставляется с g++) имеет его (см. здесь для всего исходного кода):

typedef typename _Rep_type::const_iterator            iterator;
typedef typename _Rep_type::const_iterator            const_iterator;

В SGI docs говорится:

iterator       Container  Iterator used to iterate through a set.
const_iterator Container  Const iterator used to iterate through a set. (Iterator and const_iterator are the same type.)

С другой стороны, VС++ 2008 Express компилирует ваш код, не жалуясь, что вы вызываете методы non const на set::iterator s.