помилка: передача xxx як "цього" аргументу xxx відкидає кваліфікатори


456
#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' як 'цей' аргумент 'int StudentT :: getId ()' відкидає кваліфікатори

../main.cpp:35: помилка: передача 'const StudentT' як 'цей' аргумент 'std :: string StudentT :: getName ()' відкидає кваліфікатори

Що не так з цим кодом? Дякую!


13
Де у вашому фрагменті коду рядок 35?
У силіко

117
Я б хотів, щоб GCC покращила це повідомлення про помилку, наприклад, "відкидає класифікатори" -> "порушує коректність правильності"
jfritz42

13
@ jfritz42: Буде заплутаним у справі, яку він відкидаєvolatile
PlasmaHH

3
@PlasmaHH повідомлення про помилку було б розділене на "порушення коректності коректності" та "порушення непостійної коректності". Зараз не багато людей задумаються про те, щоб щось було непостійним правильним
Калет

Відповіді:


523

Об'єкти в об'єкті std::setзберігаються як const StudentT. Отже, коли ви намагаєтеся зателефонувати getId()з constоб'єктом, компілятор виявляє проблему, в основному ви викликаєте функцію члена non-const на об'єкті const, яка заборонена, оскільки функції non-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довідковими.


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походить сюди, але я підозрюю, що setповертає ітератор посилання const, щоб запобігти зміні примірника і тим самим недійсно встановити набір.
Фред Ларсон

@Mahesh: Не пройшов перевірку коду. У мене є колега, який відноситься до мене як до "зможеного". 8v) Змініть це foo obj;на const foo obj;один раз і подивіться, що станеться. Або передайте constпосилання на foo.
Фред Ларсон

3
@Mahesh: Це, як я вже сказав, - якщо елементи в a setбудуть змінені, порядок може бути порушено, і тоді набір більше не дійсний. У map, лише ключ const. У a set, цілий об'єкт є дійсно ключовим.
Фред Ларсон

1
@Mahesh: const необхідні, інакше ви не можете їх називати об'єктами const. дивіться функцію f()в моїй відповіді.
Наваз

5

Насправді стандарт C ++ (тобто C ++ 0x чернетка ) говорить (tnx до @Xeo & @Ben Voigt, щоб вказати мені на це):

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

Тож VC ++ 2008 Dinkumware непрацездатний.


Стара відповідь:

Ви отримали цю помилку, тому що в певних реалізаціях lig-файлу 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 компілює ваш код, не поскаржившись, що ви закликаєте non-const методи для 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не хвилюйтесь щодо зміни членів у ньому.

Що стосується вашої проблеми, об'єкти в наборі std :: зберігаються як const StudentT, але метод getIdі getNameне const, тому ви даєте вищезгадану помилку.

Ви також можете побачити це питання, яке означає значення 'const' останнє у функції оголошення класу? для більш детальної інформації.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.