error: pasar xxx como argumento 'this' de xxx descarta calificadores

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

En línea:

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

Da un error que:

../main.cpp:35: error: pasar 'const StudentT' como 'este' argumento de 'int StudentT :: getId ()' descarta los calificadores

../main.cpp:35: error: pasar 'const StudentT' como 'este' argumento de 'std :: string StudentT :: getName ()' descarta los calificadores

¿Qué tiene de malo este código? ¡Gracias!

Jason
fuente
13
¿Dónde está la línea 35 en su fragmento de código?
En silico
118
Deseo que GCC mejore este mensaje de error, por ejemplo, "descarta calificadores" -> "rompe la corrección constante"
jfritz42
13
@ jfritz42: Sería confuso para el caso que descartavolatile
PlasmaHH
3
@PlasmaHH el mensaje de error se dividiría en "rompe la corrección constante" y "rompe la corrección volátil". Ahora, no muchas personas pensarán que algo es volátil correctamente
Caleth

Respuestas:

524

Los objetos en el std::setse almacenan como const StudentT. Entonces, cuando intentas llamar getId()con el constobjeto, el compilador detecta un problema, principalmente estás llamando a una función miembro no constante en el objeto constante que no está permitida porque las funciones miembro no constantes NO PROMETEN NO modificar el objeto; así que el compilador hará una suposición segura que getId()podría intentar modificar el objeto pero al mismo tiempo, también se da cuenta de que el objeto es constante; entonces cualquier intento de modificar el objeto const debería ser un error. Por lo tanto, el compilador genera un mensaje de error.

La solución es simple: hacer las funciones constantes como:

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

Esto es necesario porque ahora puede llamar getId()y getName()en objetos const 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 al margen, debe implementar operator<como:

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

Los parámetros de nota ahora son de constreferencia.

Nawaz
fuente
3
Una explicación tan clara. Gracias. Pero me pregunto acerca de su último fragmento de código. ¿Por qué usar referencia en el parámetro de función? const StudentT & s1, const StudentT & s2?
Rafael Adel
2
@RafaelAdel: se usa la referencia para evitar copias innecesarias y constdebido a que la función no necesita modificar el objeto, por lo que se constaplica en tiempo de compilación.
Nawaz
90

Las funciones miembro que no modifican la instancia de clase deben declararse como const:

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

Cada vez que ves "calificadores de descarte", se trata de consto volatile.

Fred Larson
fuente
2
@Fred: ¿crees que definitivamente es un requisito agregar modificadores constantes a las funciones miembro que no modifican la instancia de clase? ¿Hay alguna otra razón para el error en este caso? Lo dudo porque en la mayoría de los captadores que escribo, no le agrego modificadores constantes.
Mahesh
@Mahesh: Sí, es parte de la corrección constante . No estoy seguro de dónde constviene el aquí, pero sospecho que setestá devolviendo una referencia constante del iterador para evitar que la instancia cambie y, por lo tanto, invalide el conjunto.
Fred Larson
@Mahesh: No pasaría la revisión de mi código. Tengo un compañero de trabajo que se refiere a mí como el "constante". 8v) Cambie eso foo obj;a const foo obj;una vez y vea qué sucede. O pasar una constreferencia a a foo.
Fred Larson
3
@Mahesh: Es como dije: si setse cambian los elementos en a , el orden se puede alterar y el conjunto ya no es válido. En a map, solo la clave es const. En a set, todo el objeto es realmente la clave.
Fred Larson
1
@Mahesh: const son necesarios, de lo contrario no puede llamarlos con objetos const. Ver la función f()en mi respuesta.
Nawaz
5

En realidad, el estándar C ++ (es decir, C ++ 0x draft ) dice (tnx a @Xeo y @Ben Voigt por señalarme eso):

23.2.4 Contenedores asociativos
5 Para set y multiset el tipo de valor es el mismo que el tipo de clave. Para mapa y multimapa es igual a par. Las claves en un contenedor asociativo son inmutables.
6 iterador de un contenedor asociativo es de la categoría de iterador bidireccional. Para contenedores asociativos donde el tipo de valor es el mismo que el tipo de clave, tanto iterator como const_iterator son iteradores constantes. No se especifica si iterator y const_iterator son o no del mismo tipo.

Entonces, la implementación de VC ++ 2008 Dinkumware es defectuosa.


Vieja respuesta:

Obtuviste ese error porque en ciertas implementaciones de std lib set::iteratores igual que set::const_iterator.

Por ejemplo, libstdc ++ (incluido con g ++) lo tiene (consulte aquí el código fuente completo):

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

Y en los documentos de SGI dice:

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 otro lado, VC ++ 2008 Express compila su código sin quejarse de que está llamando a métodos no constantes en set::iterators.

Eugen Constantin Dinca
fuente
2

Permítanme dar un ejemplo más detallado. En cuanto a la siguiente estructura:

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

ingrese la descripción de la imagen aquí

Como ve lo anterior, el IDE (CLion) le dará consejos Non-const function 'getCount' is called on the const object. En el método add countse declara como objeto const, pero el método getCountno es const, por lo que count.getCount()puede cambiar los miembros en count.

Error de compilación como se muestra a continuación (mensaje principal en mi compilador):

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

Para resolver el problema anterior, puede:

  1. cambia el método uint32_t getCount(){...}a uint32_t getCount() const {...}. Entonces count.getCount()no cambiará a los miembros count.

o

  1. cambiar uint32_t add(const Count& count){...}a uint32_t add(Count& count){...}. Así que countno me importa cambiar miembros en él.

En cuanto a su problema, los objetos en std :: set se almacenan como const StudentT, pero son el método getIdy getNameno son const, por lo que da el error anterior.

También puede ver esta pregunta ¿Qué significa 'const' último en una declaración de función de una clase? para más detalles.

Jayhello
fuente