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

457
#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 ()' отменяет квалификаторы

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

JASON
источник
13
Где строка 35 в вашем фрагменте кода?
In silico
118
Я хотел бы, чтобы GCC улучшил это сообщение об ошибке, например, "сбросит квалификаторы" -> "нарушает правильность
констант
13
@ jfritz42: Было бы непонятно для случая, когда он отказываетсяvolatile
PlasmaHH
3
@PlasmaHH сообщение об ошибке будет разделено на «нарушает правильность» и «нарушает изменчивость». Теперь, не многие люди будут думать о чем-то, что является изменчивым, правильным
Калет

Ответы:

524

Объекты в std::setхранятся как const StudentT. Поэтому , когда вы пытаетесь вызвать getId()с constобъектом компилятор обнаруживает проблему, главным образом , вы вызываете неконстантную функцию - член константный объект , который не допускается , поскольку неконстантные функции - члены сделать NO PROMISE не изменять объект; поэтому компилятор сделает безопасное предположение, которое getId()может попытаться изменить объект, но в то же время он также заметит, что объект является 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ссылки.

Наваз
источник
3
Такое четкое объяснение. Спасибо. Но меня интересует ваш последний фрагмент кода. Зачем использовать ссылку в параметре функции? const StudentT & s1, const StudentT & s2?
Рафаэль Адель
2
@RafaelAdel: Вы используете ссылку, чтобы избежать ненужного копирования, и constпотому что функция не нуждается в изменении объекта, поэтому constобеспечивает выполнение этого во время компиляции.
Наваз
90

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

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

Каждый раз, когда вы видите «отбрасывает квалификаторы», это говорит о constили volatile.

Фред Ларсон
источник
2
@Fred - Как вы думаете, определенно необходимо добавить модификаторы const в функции-члены, которые не изменяют экземпляр класса? Есть ли какая-то другая причина ошибки в этом случае? Я сомневаюсь в этом, потому что в большинстве получателей, которые я пишу, я не добавляю к ним модификаторы const.
Махеш
@Mahesh: Да, это часть правильности const . Я не уверен, откуда constон приходит, но я подозреваю, что setон возвращает ссылку на const от итератора, чтобы предотвратить изменение экземпляра и, таким образом, сделать набор недействительным.
Фред Ларсон
@Mahesh: не прошел бы мой обзор кода. У меня есть сотрудник, который называет меня «постоянным». 8v) Измените это foo obj;на const foo obj;один раз и посмотрите, что произойдет. Или передайте constссылку на foo.
Фред Ларсон
3
@Mahesh: Как я уже сказал - если элементы в a setизменены, порядок может быть нарушен, и тогда набор больше не действителен. В map, только ключ есть const. В целом set, весь объект действительно является ключом.
Фред Ларсон
1
@Mahesh: const необходимы, иначе вы не можете вызвать их с помощью const объектов. см. функцию f()в моем ответе.
Наваз
5

На самом деле стандарт C ++ (то есть черновик C ++ 0x ) гласит (tnx to @Xeo & @Ben Voigt за указание на это мне):

23.2.4 Ассоциативные контейнеры
5 Для set и multiset тип значения совпадает с типом ключа. Для карты и мультикарты он равен паре. Ключи в ассоциативном контейнере являются неизменяемыми.
6 итератор ассоциативного контейнера относится к категории двунаправленных итераторов. Для ассоциативных контейнеров, в которых тип значения совпадает с типом ключа, итератор и const_iterator являются постоянными итераторами. Не указано, являются ли итераторы и const_iterator одинаковыми.

Таким образом, реализация Dinkumware в VC ++ 2008 неисправна.


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

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

Например, libstdc ++ (поставляется с g ++) имеет его (см. Здесь весь исходный код):

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

И в документах SGI говорится:

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.)

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

Евгений Константин Динка
источник
2

Позвольте мне привести более подробный пример. Что касается структуры ниже:

struct Count{
    uint32_t c;

    Count(uint32_t i=0):c(i){}

    uint32_t getCount(){
        return c;
    }

    uint32_t add(const Count& count){
        uint32_t total = c + count.getCount();
        return total;
    }
};

введите описание изображения здесь

Как вы видите выше, IDE (CLion) даст подсказки Non-const function 'getCount' is called on the const object. В методе add countобъявлен как const объект, но метод getCountне является методом const, поэтому count.getCount()может изменять члены в count.

Ошибка компиляции, как показано ниже (основное сообщение в моем компиляторе):

error: passing 'const xy_stl::Count' as 'this' argument discards qualifiers [-fpermissive]

Чтобы решить вышеуказанную проблему, вы можете:

  1. измените метод uint32_t getCount(){...}на uint32_t getCount() const {...}. Так count.getCount()что не буду менять членов count.

или

  1. изменить uint32_t add(const Count& count){...}на uint32_t add(Count& count){...}. Так countчто не волнуйтесь о смене членов в нем.

Что касается вас проблем, объекты в станде :: набор сохраняется как константа StudentT, но метод getIdи getNameне сопзИте, поэтому вы даете вышеуказанную ошибку.

Вы также можете увидеть этот вопрос Значение 'const' последний в объявлении функции класса? для более подробной информации.

Jayhello
источник