galat: melewatkan xxx sebagai argumen 'ini' dari kualifikasi yang dibuang 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;
}

Di barisan:

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

Ini memberikan kesalahan bahwa:

../main.cpp:35: error: meneruskan 'const StudentT' sebagai argumen 'this' dari 'int StudentT :: getId ()' membuang kualifikasi

../main.cpp:35: error: meneruskan 'const StudentT' sebagai argumen 'this' dari 'std :: string StudentT :: getName ()' membuang kualifikasi

Apa yang salah dengan kode ini? Terima kasih!


13
Di mana baris 35 dalam cuplikan kode Anda?
Di silico

118
Saya berharap GCC akan meningkatkan pesan kesalahan ini, misalnya "membuang kualifikasi" -> "merusak kebenaran const"
jfritz42

13
@ jfritz42: Akan membingungkan untuk kasus yang volatile
dibuangnya

3
@PlasmaHH pesan kesalahan akan dipecah menjadi "break true const" dan "break true volatile". Sekarang, tidak banyak orang akan berpikir tentang sesuatu yang volatile benar
Caleth

Jawaban:


524

Objek dalam std::setdisimpan sebagai const StudentT. Jadi, ketika Anda mencoba untuk memanggil getId()dengan constobyek compiler mendeteksi masalah, terutama Anda menelepon non-const fungsi anggota pada objek const yang tidak diperbolehkan karena non-const fungsi anggota membuat NO PROMISE untuk tidak memodifikasi objek; jadi kompiler akan membuat asumsi yang aman yang getId()mungkin mencoba untuk memodifikasi objek tetapi pada saat yang sama, ia juga memperhatikan bahwa objek tersebut adalah const; jadi setiap upaya untuk memodifikasi objek const harus menjadi kesalahan. Karenanya kompiler menghasilkan pesan kesalahan.

Solusinya sederhana: buat fungsi const sebagai:

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

Ini diperlukan karena sekarang Anda dapat memanggil getId()dan getName()objek const sebagai:

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
}

Sebagai sidenote, Anda harus menerapkan operator<sebagai:

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

Parameter note sekarang constreferensi.


3
Penjelasan yang jelas. Terima kasih. Tapi saya ingin tahu tentang cuplikan kode terakhir Anda. Mengapa menggunakan referensi dalam parameter fungsi? const StudentT & s1, const StudentT & s2?
Rafael Adel

2
@ RafaelAdel: Anda menggunakan referensi untuk menghindari penyalinan yang tidak perlu, dan constkarena fungsi tidak perlu memodifikasi objek, jadi constini memberlakukan ini pada waktu kompilasi.
Nawaz

90

Fungsi anggota yang tidak memodifikasi instance kelas harus dinyatakan sebagai const:

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

Setiap kali Anda melihat "membuang kualifikasi", itu berbicara tentang constatau volatile.


2
@ Fred - Apakah Anda pikir itu adalah persyaratan untuk menambahkan pengubah konstanta ke fungsi anggota yang tidak memodifikasi instance kelas? Apakah ada alasan lain untuk kesalahan dalam hal ini? Saya ragu karena di sebagian besar getter yang saya tulis, saya tidak menambahkan pengubah const ke dalamnya.
Mahesh

@ Mahesh: Ya, itu bagian dari kebenaran const . Saya tidak yakin dari mana constdatangnya dari sini, tapi saya curiga setmengembalikan referensi const dari iterator untuk mencegah instance berubah dan dengan demikian membatalkan set.
Fred Larson

@ Mahesh: Tidak akan melewatkan ulasan kode saya. Saya memiliki rekan kerja yang menyebut saya sebagai "yang mampu". 8v) Ubah itu foo obj;menjadi const foo obj;sekali dan lihat apa yang terjadi. Atau berikan constreferensi ke a foo.
Fred Larson

3
@ Mahesh: Seperti yang saya katakan - jika elemen-elemen dalam a setdiubah, urutannya bisa kesal dan set tidak lagi valid. Dalam a map, hanya kuncinya const. Dalam a set, seluruh objek adalah kuncinya.
Fred Larson

1
@ Mahesh: const diperlukan jika tidak, Anda tidak dapat memanggil mereka dengan objek const. lihat fungsi f()dalam jawaban saya.
Nawaz

5

Sebenarnya standar C ++ (yaitu konsep C ++ 0x ) mengatakan (tnx ke @Xeo & @Ben Voigt untuk menunjukkan hal itu kepada saya):

23.2.4 Wadah asosiatif
5 Untuk set dan multiset, tipe nilai sama dengan tipe kunci. Untuk peta dan multimap sama dengan berpasangan. Kunci dalam wadah asosiatif tidak dapat diubah.
6 iterator dari wadah asosiatif adalah dari kategori iterator dua arah. Untuk wadah asosiatif di mana jenis nilai sama dengan jenis kunci, baik iterator dan const_iterator adalah iterator konstan. Tidak ditentukan apakah iterator dan const_iterator memiliki tipe yang sama atau tidak.

Jadi implementasi Dinkumware VC ++ 2008 salah.


Jawaban lama:

Anda mendapatkan kesalahan itu karena dalam implementasi tertentu dari std lib set::iteratorsama dengan set::const_iterator.

Misalnya libstdc ++ (dikirimkan bersama g ++) memilikinya (lihat di sini untuk seluruh kode sumber):

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

Dan dalam dokumen SGI disebutkan:

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

Di sisi lain VC ++ 2008 Express mengkompilasi kode Anda tanpa mengeluh bahwa Anda memanggil metode non const pada set::iterators.


2

Mari saya beri contoh yang lebih detail. Adapun struct di bawah ini:

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;
    }
};

masukkan deskripsi gambar di sini

Seperti yang Anda lihat di atas, IDE (CLion), akan memberikan tips Non-const function 'getCount' is called on the const object. Dalam metode add countini dinyatakan sebagai objek const, tetapi metode getCountini bukan metode const, jadi count.getCount()dapat mengubah anggota dalam count.

Kompilasi kesalahan seperti di bawah ini (pesan inti di kompiler saya):

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

Untuk mengatasi masalah di atas, Anda dapat:

  1. ubah metode uint32_t getCount(){...}menjadi uint32_t getCount() const {...}. Jadi count.getCount()tidak akan mengubah anggota count.

atau

  1. ubah uint32_t add(const Count& count){...}ke uint32_t add(Count& count){...}. Jadi, countjangan pedulikan mengubah anggota di dalamnya.

Mengenai masalah Anda, objek di std :: set disimpan sebagai const StudentT, tetapi metode getIddan getNamebukan const, jadi Anda memberikan kesalahan di atas.

Anda juga dapat melihat pertanyaan ini Arti dari 'const' terakhir dalam deklarasi fungsi suatu kelas? untuk lebih detail.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.