Fehler: Wenn Sie xxx als 'this'-Argument von xxx übergeben, werden die Qualifizierer verworfen

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 der Reihe:

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

Es gibt einen Fehler, dass:

../main.cpp:35: Fehler: Beim Übergeben von 'const StudentT' als 'this'-Argument von' int StudentT :: getId () 'werden Qualifizierer verworfen

../main.cpp:35: Fehler: Beim Übergeben von 'const StudentT' als 'this'-Argument von' std :: string StudentT :: getName () 'werden Qualifizierer verworfen

Was ist los mit diesem Code? Vielen Dank!

JASON
quelle
13
Wo befindet sich Zeile 35 in Ihrem Code-Snippet?
In silico
117
Ich wünschte, GCC würde diese Fehlermeldung verbessern, z. B. "Qualifizierer
verwerfen
13
@ jfritz42: Wäre verwirrend für den Fall, dass es volatile
verworfen wird
3
@PlasmaHH Die Fehlermeldung würde in "Unterbrechungen der Konstantenkorrektheit" und "Unterbrechungen der flüchtigen Korrektheit" aufgeteilt. Jetzt werden nicht viele Leute darüber nachdenken, dass etwas flüchtig ist
Caleth

Antworten:

524

Die Objekte in std::setwerden als gespeichert const StudentT. Wenn Sie also versuchen, getId()mit dem constObjekt aufzurufen, erkennt der Compiler ein Problem. Sie rufen hauptsächlich eine Nicht-Const-Member-Funktion für ein Const-Objekt auf, die nicht zulässig ist, da Nicht-Const-Member-Funktionen KEIN VERSPRECHEN machen, das Objekt nicht zu ändern. Der Compiler geht also von einer sicheren Annahme aus, die getId()möglicherweise versucht, das Objekt zu ändern, stellt jedoch gleichzeitig fest, dass das Objekt const ist. Daher sollte jeder Versuch, das const-Objekt zu ändern, ein Fehler sein. Daher generiert der Compiler eine Fehlermeldung.

Die Lösung ist einfach: Machen Sie die Funktionen const wie folgt:

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

Dies ist notwendig , denn jetzt können Sie anrufen getId()und getName()auf const Objekte wie:

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
}

Als Nebenbemerkung sollten Sie Folgendes implementieren operator<:

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

Hinweis Parameter sind jetzt constReferenz.

Nawaz
quelle
3
So eine klare Erklärung. Vielen Dank. Aber ich wundere mich über Ihren letzten Code-Ausschnitt. Warum Referenz im Funktionsparameter verwenden? const StudentT & s1, const StudentT & s2?
Rafael Adel
2
@RafaelAdel: Sie verwenden Verweise, um unnötiges Kopieren zu vermeiden. Da constdie Funktion das Objekt nicht ändern muss, wird constdies beim Kompilieren erzwungen .
Nawaz
90

Mitgliedsfunktionen, die die Klasseninstanz nicht ändern, sollten wie folgt deklariert werden const:

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

Immer wenn Sie "Qualifikationsmerkmale verwerfen" sehen, geht es um constoder volatile.

Fred Larson
quelle
2
@Fred - Glauben Sie, dass es definitiv erforderlich ist, Mitgliedsfunktionen, die die Klasseninstanz nicht ändern, const-Modifikatoren hinzuzufügen? Gibt es in diesem Fall einen anderen Grund für den Fehler? Ich bezweifle es, weil ich in den meisten Gettern, die ich schreibe, keine const-Modifikatoren hinzufüge.
Mahesh
@Mahesh: Ja, es ist ein Teil von const Korrektheit . Ich bin nicht sicher, woher das constkommt, aber ich vermute, dass das seteine konstante Referenz vom Iterator zurückgibt, um zu verhindern, dass sich die Instanz ändert und dadurch die Menge ungültig macht.
Fred Larson
@ Mahesh: Ich würde meine Codeüberprüfung nicht bestehen. Ich habe einen Kollegen, der mich als "Constable" bezeichnet. 8v) Ändern Sie das foo obj;auf const foo obj;einmal und sehen Sie, was passiert. Oder geben Sie einen constVerweis auf a foo.
Fred Larson
3
@ Mahesh: Es ist wie gesagt - wenn die Elemente in a setgeändert werden, kann die Reihenfolge gestört werden und dann ist die Menge nicht mehr gültig. In a mapist nur der Schlüssel const. In a setist das ganze Objekt wirklich der Schlüssel.
Fred Larson
1
@ Mahesh: const sind notwendig, sonst kannst du sie nicht mit const Objekten aufrufen. siehe die Funktion f()in meiner Antwort.
Nawaz
5

Tatsächlich sagt der C ++ - Standard (dh C ++ 0x Entwurf ) (tnx an @Xeo & @Ben Voigt, um mich darauf hinzuweisen):

23.2.4 Assoziative Container
5 Für Set und Multiset entspricht der Werttyp dem Schlüsseltyp. Für Map und Multimap ist es gleich Pair. Schlüssel in einem assoziativen Container sind unveränderlich.
6 Der Iterator eines assoziativen Containers gehört zur Kategorie der bidirektionalen Iteratoren. Bei assoziativen Containern, bei denen der Werttyp mit dem Schlüsseltyp identisch ist, sind sowohl der Iterator als auch der const_iterator konstante Iteratoren. Es ist nicht angegeben, ob Iterator und const_iterator vom gleichen Typ sind.

Die Implementierung von VC ++ 2008 Dinkumware ist also fehlerhaft.


Alte Antwort:

Sie haben diesen Fehler erhalten, weil in bestimmten Implementierungen der std lib set::iteratordas gleiche ist wie set::const_iterator.

Zum Beispiel hat libstdc ++ (im Lieferumfang von g ++ enthalten) es (siehe hier für den gesamten Quellcode):

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

In den Dokumenten von SGI heißt es:

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

Andererseits kompiliert VC ++ 2008 Express Ihren Code, ohne sich darüber zu beschweren, dass Sie nicht konstante Methoden für set::iterators aufrufen .

Eugen Constantin Dinca
quelle
2

Lassen Sie mich ein detaillierteres Beispiel geben. Zur folgenden Struktur:

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

Geben Sie hier die Bildbeschreibung ein

Wie Sie oben sehen, gibt die IDE (CLion) Tipps Non-const function 'getCount' is called on the const object. In der Methode add countwird als const-Objekt deklariert, aber die Methode getCountist keine const-Methode, daher count.getCount()können die Mitglieder in geändert werden count.

Kompilierungsfehler wie folgt (Kernmeldung in meinem Compiler):

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

Um das oben genannte Problem zu lösen, können Sie:

  1. Ändern Sie die Methode uint32_t getCount(){...}in uint32_t getCount() const {...}. Also count.getCount()werden die Mitglieder nicht geändert count.

oder

  1. wechseln uint32_t add(const Count& count){...}zu uint32_t add(Count& count){...}. Es ist also countegal, ob Sie die Mitglieder wechseln.

In Bezug auf Ihr Problem werden Objekte im std :: set als const StudentT gespeichert, aber die Methode getIdund getNamesind nicht const, daher geben Sie den obigen Fehler an.

Sie können diese Frage auch sehen. Bedeutung von 'const' zuletzt in einer Funktionsdeklaration einer Klasse? für mehr Details.

Jayhello
quelle