erreur: passer xxx comme argument 'this' de xxx supprime les qualificatifs

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 ligne:

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

Cela donne une erreur qui:

../main.cpp:35: erreur: passer 'const StudentT' comme 'cet' argument de 'int StudentT :: getId ()' élimine les qualificatifs

../main.cpp:35: erreur: passer 'const StudentT' comme 'cet' argument de 'std :: string StudentT :: getName ()' supprime les qualificatifs

Quel est le problème avec ce code? Je vous remercie!

JASON
la source
13
Où se trouve la ligne 35 dans votre extrait de code?
In silico
118
Je souhaite que GCC améliore ce message d'erreur, par exemple "élimine les qualificatifs" -> "casse l'exactitude de la const"
jfritz42
13
@ jfritz42: Serait déroutant pour le cas qu'il jettevolatile
PlasmaHH
3
@PlasmaHH, le message d'erreur serait divisé en «casse l'exactitude constante» et «casse l'exactitude volatile». Maintenant, peu de gens penseront à quelque chose de volatile correct
Caleth

Réponses:

524

Les objets du std::setsont stockés sous const StudentT. Ainsi, lorsque vous essayez d'appeler getId()avec l' constobjet, le compilateur détecte un problème, vous appelez principalement une fonction membre non const sur un objet const qui n'est pas autorisé car les fonctions membres non const ne font AUCUNE PROMESSE de ne pas modifier l'objet; donc le compilateur va faire une hypothèse sûre qui getId()pourrait tenter de modifier l'objet mais en même temps, il remarque également que l'objet est const; toute tentative de modification de l'objet const doit donc être une erreur. Par conséquent, le compilateur génère un message d'erreur.

La solution est simple: rendre les fonctions constantes comme:

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

Ceci est nécessaire car vous pouvez maintenant appeler getId()et getName()sur des objets const comme:

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
}

En tant que sidenote, vous devez implémenter en operator<tant que:

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

Les paramètres de note sont désormais des constréférences.

Nawaz
la source
3
Une explication si claire. Merci. Mais je me pose des questions sur votre dernier extrait de code. Pourquoi utiliser la référence dans le paramètre de fonction? const StudentT & s1, const StudentT & s2?
Rafael Adel
2
@RafaelAdel: Vous utilisez la référence pour éviter la copie inutile, et constparce que la fonction n'a pas besoin de modifier l'objet, donc elle constapplique cela au moment de la compilation.
Nawaz
90

Les fonctions membres qui ne modifient pas l'instance de classe doivent être déclarées comme const:

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

Chaque fois que vous voyez "défausse les qualificatifs", il s'agit de constou volatile.

Fred Larson
la source
2
@Fred - Pensez-vous qu'il est absolument nécessaire d'ajouter des modificateurs const aux fonctions membres qui ne modifient pas l'instance de classe? Y a-t-il une autre raison de l'erreur dans ce cas? J'en doute car dans la plupart des getters que j'écris, je n'y ajoute pas de modificateurs const.
Mahesh
@ Mahesh: Oui, cela fait partie de l' exactitude des const . Je ne suis pas certain d'où constvient d'ici, mais je soupçonne que le setretourne une référence const de l'itérateur pour empêcher l'instance de changer et ainsi invalider l'ensemble.
Fred Larson
@ Mahesh: Je ne passerais pas mon examen de code. J'ai un collègue qui me désigne comme le «constable». 8v) Changez cela foo obj;en const foo obj;une fois et voyez ce qui se passe. Ou passez une constréférence à a foo.
Fred Larson
3
@Mahesh: C'est comme je l'ai dit - si les éléments dans a setsont modifiés, l'ordre peut être bouleversé, puis l'ensemble n'est plus valide. Dans un map, seule la clé est const. Dans un set, tout l'objet est vraiment la clé.
Fred Larson
1
@Mahesh: const sont nécessaires sinon vous ne pouvez pas les appeler avec des objets const. voir la fonction f()dans ma réponse.
Nawaz
5

En fait, la norme C ++ (c'est-à-dire le projet C ++ 0x ) dit (tnx à @Xeo & @Ben Voigt pour me l'avoir signalé):

23.2.4 Conteneurs associatifs
5 Pour les ensembles et les ensembles multiples, le type de valeur est le même que le type de clé. Pour la carte et le multimap, c'est égal à la paire Les clés d'un conteneur associatif sont immuables.
6 L' itérateur d'un conteneur associatif est de la catégorie des itérateurs bidirectionnels. Pour les conteneurs associatifs où le type de valeur est le même que le type de clé, itérateur et const_iterator sont des itérateurs constants. Il n'est pas spécifié si l'itérateur et le const_iterator sont du même type.

La mise en œuvre de VC ++ 2008 Dinkumware est donc défectueuse.


Ancienne réponse:

Vous avez cette erreur , car dans certaines implémentations de la lib std l' set::iteratorest le même que set::const_iterator.

Par exemple, libstdc ++ (livré avec g ++) l'a (voir ici pour le code source complet):

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

Et dans les documents de SGI, il indique:

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'autre part, VC ++ 2008 Express compile votre code sans se plaindre d'appeler des méthodes non const sur set::iterators.

Eugen Constantin Dinca
la source
2

Permettez-moi de donner un exemple plus détaillé. Quant à la structure ci-dessous:

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

entrez la description de l'image ici

Comme vous le voyez ci-dessus, l'IDE (CLion), vous donnera des conseils Non-const function 'getCount' is called on the const object. Dans la méthode add countest déclarée comme objet const, mais la méthode getCountn'est pas une méthode const, donc count.getCount()peut changer les membres dans count.

Erreur de compilation comme ci-dessous (message principal dans mon compilateur):

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

Pour résoudre le problème ci-dessus, vous pouvez:

  1. changer la méthode uint32_t getCount(){...}en uint32_t getCount() const {...}. count.getCount()Ne changera donc pas les membres count.

ou

  1. changer uint32_t add(const Count& count){...}pour uint32_t add(Count& count){...}. Donc, countne vous souciez pas de changer de membre.

Quant à votre problème, les objets dans le std :: set sont stockés en tant que const StudentT, mais la méthode getIdet getNamene sont pas const, vous donnez donc l'erreur ci-dessus.

Vous pouvez également voir cette question Signification de «const» en dernier dans une déclaration de fonction d'une classe? pour plus de détails.

Jayhello
la source