erro: passar xxx como argumento 'this' de xxx descarta qualificadores

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

Na linha:

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

Dá um erro que:

../main.cpp:35: erro: passar 'const StudentT' como 'this' argumento de 'int StudentT :: getId ()' descarta qualificadores

../main.cpp:35: erro: passar 'const StudentT' como 'this' argumento de 'std :: string StudentT :: getName ()' descarta qualificadores

O que há de errado com este código? Obrigado!

JASON
fonte
13
Onde está a linha 35 no seu snippet de código?
In silico
118
Desejo GCC iria melhorar esta mensagem de erro, por exemplo, "descarta qualificadores" -> "breaks const correção"
jfritz42
13
@ jfritz42: seria confuso para o caso, descartavolatile
PlasmaHH
3
@PlasmaHH, a mensagem de erro seria dividida em "quebra a correção da const" e "quebra a correção da volátil". Agora, muitas pessoas não pensam em algo volátil correto
#

Respostas:

524

Os objetos no std::setsão armazenados como const StudentT. Portanto, quando você tenta chamar getId()o constobjeto, o compilador detecta um problema, principalmente você está chamando uma função de membro não-const no objeto const, o que não é permitido porque as funções de membro que não fazem const NÃO prometem não modificar o objeto; portanto, o compilador fará uma suposição segura que getId()pode tentar modificar o objeto, mas, ao mesmo tempo, também nota que o objeto é const; portanto, qualquer tentativa de modificar o objeto const deve ser um erro. Portanto, o compilador gera uma mensagem de erro.

A solução é simples: torne as funções constantes como:

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

Isso é necessário porque agora você pode chamar getId()e getName()const objetos como:

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
}

Como nota de rodapé, você deve implementar operator<como:

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

Os parâmetros de nota agora são constreferência.

Nawaz
fonte
3
Uma explicação tão clara. Obrigado. Mas eu me pergunto sobre o seu último trecho de código. Por que usar referência no parâmetro de função? const StudentT & s1, const StudentT & s2?
Rafael Adel
2
@RafaelAdel: Você usa referência para evitar cópias desnecessárias e, constcomo a função não precisa modificar o objeto, a aplicação é constimposta no momento da compilação.
precisa
90

As funções de membro que não modificam a instância da classe devem ser declaradas como const:

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

Sempre que você vê "descarta qualificadores", está falando sobre constou volatile.

Fred Larson
fonte
2
@ Fr - Você acha que é definitivamente um requisito adicionar modificadores const às funções de membros que não modificam a instância da classe? Existe alguma outra razão para o erro neste caso? Duvido, porque na maioria dos getters que escrevo, não adiciono modificadores const a ele.
Mahesh
@ Mahesh: Sim, faz parte da correção constante . Não sei ao certo de onde constestá vindo, mas suspeito que setesteja retornando uma referência const do iterador para impedir que a instância seja alterada e, assim, invalidar o conjunto.
Fred Larson
@ Mahesh: Não passaria na minha revisão de código. Eu tenho um colega de trabalho que se refere a mim como o "constante". 8v) Mude isso foo obj;para const foo obj;uma vez e veja o que acontece. Ou passe uma constreferência para a foo.
Fred Larson
3
@Mahesh: É como eu disse - se os elementos em um setforem alterados, a ordem poderá ser alterada e o conjunto não será mais válido. Em a map, apenas a chave é const. Em a set, todo o objeto é realmente a chave.
Fred Larson
1
@Mahesh: const são necessários, caso contrário você não pode chamá-los com objetos const. veja a função f()na minha resposta.
Nawaz
5

Na verdade, o padrão C ++ (ou seja, rascunho C ++ 0x ) diz (tnx para @Xeo e @Ben Voigt por apontar isso para mim):

23.2.4 Contêineres associativos
5 Para definir e multiset, o tipo de valor é igual ao tipo de chave. Para mapa e multimapa, é igual ao par. As chaves em um contêiner associativo são imutáveis.
O iterador 6 de um contêiner associativo é da categoria de iterador bidirecional. Para contêineres associativos em que o tipo de valor é igual ao tipo de chave, o iterador e o const_iterator são iteradores constantes. Não é especificado se o iterador e o const_iterator são ou não do mesmo tipo.

Portanto, a implementação do VC ++ 2008 Dinkumware está com defeito.


Resposta antiga:

Você recebeu esse erro porque em certas implementações da lib std set::iteratoré o mesmo que set::const_iterator.

Por exemplo, o libstdc ++ (fornecido com o g ++) possui (veja aqui o código-fonte inteiro):

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

E nos documentos da SGI , afirma:

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

Por outro lado, o VC ++ 2008 Express compila seu código sem reclamar que você está chamando métodos não const em set::iterators.

Eugen Constantin Dinca
fonte
2

Vamos dar um exemplo mais detalhado. Quanto à estrutura abaixo:

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

insira a descrição da imagem aqui

Como você vê acima, o IDE (CLion), dará dicas Non-const function 'getCount' is called on the const object. No método add counté declarado como objeto const, mas o método getCountnão é const, portanto, count.getCount()pode alterar os membros emcount .

Erro de compilação como abaixo (mensagem principal no meu compilador):

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

Para resolver o problema acima, você pode:

  1. mude o método uint32_t getCount(){...}para uint32_t getCount() const {...}. Então count.getCount()não vai mudar os membros count.

ou

  1. mude uint32_t add(const Count& count){...}para uint32_t add(Count& count){...}. Portanto count, não se importe em mudar de membro.

Quanto ao seu problema, os objetos no std :: set são armazenados como const StudentT, mas o método getIdegetName não são const, portanto, você fornece o erro acima.

Você também pode ver esta pergunta Significado de 'const' por último em uma declaração de função de uma classe? para mais detalhes.

Jayhello
fonte