errore: passando xxx come argomento "questo" di xxx scarta i qualificatori


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

In linea:

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

Dà un errore che:

../main.cpp:35: errore: passando 'const StudentT' come 'questo' argomento di 'int StudentT :: getId ()' scarta i qualificatori

../main.cpp:35: errore: passando 'const StudentT' come 'questo' argomento di 'std :: string StudentT :: getName ()' scarta i qualificatori

Cosa c'è che non va in questo codice? Grazie!


13
Dov'è la riga 35 nel frammento di codice?
In silico,

118
Vorrei che GCC migliorasse questo messaggio di errore, ad esempio "scarta qualificatori" -> "break const correttezza"
jfritz42

13
@ jfritz42: sarebbe fonte di confusione per il caso che viene volatile
scartato

3
@PlasmaHH il messaggio di errore verrebbe suddiviso in "rompe la correttezza const" e "rompe la volatilità della correttezza". Ora, non molte persone penseranno che qualcosa sia volatile, corretto
Caleth,

Risposte:


524

Gli oggetti in std::setsono memorizzati come const StudentT. Quindi, quando si tenta di chiamare getId()con l' constoggetto, il compilatore rileva un problema, principalmente si chiama una funzione membro non const sull'oggetto const che non è consentita perché le funzioni membro non const non fanno PROMESSA di non modificare l'oggetto; quindi il compilatore farà un presupposto sicuro che getId()potrebbe tentare di modificare l'oggetto ma allo stesso tempo, nota anche che l'oggetto è const; quindi ogni tentativo di modificare l'oggetto const dovrebbe essere un errore. Quindi il compilatore genera un messaggio di errore.

La soluzione è semplice: rendere costanti le funzioni come:

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

Questo è necessario perché ora puoi chiamare getId()e getName()su oggetti const come:

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
}

Come sidenote, dovresti implementare operator<come:

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

I parametri della nota sono ora di constriferimento.


3
Una spiegazione così chiara. Grazie. Ma mi chiedo il tuo ultimo frammento di codice. Perché usare il riferimento nel parametro della funzione? const StudentT & s1, const StudentT & s2?
Rafael Adel,

2
@RafaelAdel: si utilizza il riferimento per evitare copie non necessarie e constpoiché la funzione non ha bisogno di modificare l'oggetto, quindi si constimpone al momento della compilazione.
Nawaz,

90

Le funzioni membro che non modificano l'istanza di classe devono essere dichiarate come const:

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

Ogni volta che vedi "scarta qualificatori", si parla di consto volatile.


2
@Fred: pensi sia assolutamente necessario aggiungere modificatori const alle funzioni membro che non modificano l'istanza della classe? C'è qualche altro motivo per l'errore in questo caso? Ne dubito perché nella maggior parte dei getter che scrivo, non aggiungo modificatori const ad esso.
Mahesh,

@Mahesh: Sì, fa parte della costante const . Non sono sicuro da dove constprovenga il file da qui, ma sospetto che setstia restituendo un riferimento const dall'iteratore per impedire all'istanza di cambiare e quindi invalidare il set.
Fred Larson,

@Mahesh: non supererei la mia recensione del codice. Ho un collega che si riferisce a me come il "costante". 8v) Cambialo foo obj;in const foo obj;una volta e vedi cosa succede. Oppure passa un constriferimento a foo.
Fred Larson,

3
@Mahesh: È come ho detto - se gli elementi in a setvengono cambiati, l'ordine può essere sconvolto e quindi il set non è più valido. In a map, solo la chiave è const. In a set, l'intero oggetto è davvero la chiave.
Fred Larson,

1
@Mahesh: const sono necessari altrimenti non puoi chiamarli con oggetti const. vedere la funzione f()nella mia risposta.
Nawaz,

5

In realtà lo standard C ++ (ovvero C ++ 0x draft ) dice (tnx a @Xeo e @Ben Voigt per avermelo fatto notare):

23.2.4 Contenitori associativi
5 Per set e multiset il tipo di valore è uguale al tipo di chiave. Per mappa e multimappa è uguale a coppia. Le chiavi in ​​un contenitore associativo sono immutabili.
6 iteratore di un contenitore associativo appartiene alla categoria iteratore bidirezionale. Per i contenitori associativi in ​​cui il tipo di valore è uguale al tipo di chiave, sia iteratore che const_iterator sono iteratori costanti. Non è specificato se iteratore e const_iterator siano o meno dello stesso tipo.

Quindi l'implementazione di Dinkumware di VC ++ 2008 è difettosa.


Vecchia risposta:

Hai ottenuto quell'errore perché in alcune implementazioni della libreria std set::iteratorè uguale a set::const_iterator.

Ad esempio libstdc ++ (fornito con g ++) ce l'ha (vedi qui per l'intero codice sorgente):

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

E nei documenti di SGI afferma:

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

D'altra parte VC ++ 2008 Express compila il tuo codice senza lamentarti del fatto che stai chiamando metodi non const su set::iterators.


2

Vorrei fare un esempio più dettagliato. Per quanto riguarda la seguente struttura:

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

inserisci qui la descrizione dell'immagine

Come vedi sopra, l'IDE (CLion) fornirà suggerimenti Non-const function 'getCount' is called on the const object. Nel metodo add countviene dichiarato come oggetto const, ma il metodo getCountnon è il metodo const, quindi è count.getCount()possibile che i membri vengano modificati count.

Errore di compilazione come di seguito (messaggio principale nel mio compilatore):

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

Per risolvere il problema sopra, puoi:

  1. cambia il metodo uint32_t getCount(){...}in uint32_t getCount() const {...}. Quindi count.getCount()non cambierà i membri in count.

o

  1. cambia uint32_t add(const Count& count){...}in uint32_t add(Count& count){...}. Quindi countnon preoccuparti di cambiare membri.

Per quanto riguarda il problema, gli oggetti nello std :: set sono memorizzati come const StudentT, ma il metodo getIde getNamenon sono const, quindi si dà l'errore sopra riportato.

Puoi anche vedere questa domanda Significato di 'const' last in una dichiarazione di funzione di una classe? per maggiori dettagli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.