C ++ - Kartenzugriff verwirft Qualifizierer (const)

113

Der folgende Code besagt, dass beim Übergeben der Karte an constdie operator[]Methode Qualifizierer verworfen werden:

#include <iostream>
#include <map>
#include <string>

using namespace std;

class MapWrapper {
public:
    const int &get_value(const int &key) const {
        return _map[key];
    }

private:
    map<int, int> _map;
};

int main() {
    MapWrapper mw;
    cout << mw.get_value(42) << endl;
    return 0;
}

Liegt dies an der möglichen Zuordnung, die beim Kartenzugriff auftritt? Können keine Funktionen mit Kartenzugriffen als const deklariert werden?

MapWrapper.cpp:10: error: passing ‘const std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >’ as ‘this’ argument of ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = int, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, int> >]’ discards qualifiers

cdleary
quelle
Nur ein Trottel, aber mw kann einfach als MapWrapper mw deklariert werden.
Luke
Guter Punkt - ich schreibe in ein paar Sprachen, daher neige ich dazu, die Syntax über sie hinweg zu normalisieren, damit sie alle in meinen Kopf passen. :)
cdleary
Ich kann das schätzen. Seien Sie jedoch vorsichtig, in Fällen wie diesem haben Sie eine zusätzliche Objektkonstruktion und -zuweisung, die nicht erforderlich sind.
Luke
Ein weiterer guter Punkt - das Verlassen auf den Standardzuweisungsoperator ist für öffentliche Beispiele keine gute Vorgehensweise. ;)
cdleary

Antworten:

152

std::map's operator []wird nicht als deklariert constund kann nicht auf sein Verhalten zurückzuführen sein:

T & operator [] (const Key & key)

Gibt einen Verweis auf den Wert zurück, der einem Schlüssel zugeordnet ist, der dem Schlüssel entspricht, und führt das Einfügen durch, falls dieser Schlüssel noch nicht vorhanden ist.

Infolgedessen kann Ihre Funktion nicht deklariert constwerden und verwendet die Karte operator[].

std::mapMit derfind() Funktion können Sie einen Schlüssel nachschlagen, ohne die Karte zu ändern.

find()Gibt ein iteratoroder const_iteratorzu einem zurück, std::pairdas sowohl den Schlüssel ( .first) als auch den Wert ( .second) enthält.

In C ++ 11 können Sie auch at()für verwenden std::map. Wenn das Element nicht vorhanden ist, löst die Funktion std::out_of_rangeim Gegensatz zu eine Ausnahme aus operator [].

Luke
quelle
8
zusätzlich: VALUE = map.find (KEY) -> second; Ich musste lernen, dass 'find ()' einen Iterator zurückgibt, der vom Typ Paar ist.
FlipMcF
5
Ich würde hinzufügen, dass Sie jetzt in C11 Folgendes verwenden können: std :: map :: at (Schlüssel) und den Iterator vermeiden.
Juan Besa
3
Interessant. Ich würde denken, C ++ würde zwischen lvalue operator[](zB foo[bar] = baz) und rvalue operator[](zB x = foo[bar]) unterscheiden - letzteres könnte sicherlich const sein.
Claudiu
15

Da operator[]es keine const-qualifizierte Überladung gibt, kann sie nicht sicher in einer const-qualifizierten Funktion verwendet werden. Dies liegt wahrscheinlich daran, dass die aktuelle Überlastung mit dem Ziel erstellt wurde, Schlüsselwerte zurückzugeben und festzulegen.

Stattdessen können Sie Folgendes verwenden:

VALUE = map.find(KEY)->second;

In C ++ 11 können Sie den at()Operator verwenden:

VALUE = map.at(KEY);
Richard
quelle
map.find(KEY)->second; ist unsicher, wenn die Kartenwerte Zeichenfolgen sind. Es neigt dazu, Müll zu drucken, wenn der SCHLÜSSEL nicht gefunden wird.
Syam
1
Das ist eine richtig erklärte Antwort, die auf den Punkt kommt. Gestern habe ich 2 Stunden lang versucht herauszufinden, was mit einem ähnlichen Fall los war. Können wir uns darauf einigen, dass die Fehlermeldung bestenfalls irreführend ist? Ich könnte viel klarer sein, wenn es nicht das Wort 'dies' hätte und anstelle des allgemeineren Qualifikators auf Konstanz Bezug nehmen würde .
Karneval
11

Sie können nicht operator[]auf einer Karte verwenden, constda diese Methode nicht constdazu dient, die Karte zu ändern (die Sie zuweisen können _map[key]). Versuchen Sie stattdessen die findMethode.

nlativy
quelle
1
Zur Erklärung: Was soll der Operator [] der Karte tun, wenn der Schlüssel nicht vorhanden ist? Wenn die Zuordnung nicht konstant ist, wird der Schlüssel mit dem standardmäßig erstellten Wert hinzugefügt. Wenn die Map const ist, was kann der Operator [] zurückgeben? Dieser Schlüssel enthält keinen Wert.
Das ist eine richtig erklärte Antwort, die auf den Punkt kommt. Gestern habe ich 2 Stunden lang versucht herauszufinden, was mit einem ähnlichen Fall los war. Können wir uns darauf einigen, dass die Fehlermeldung bestenfalls irreführend ist? Ich könnte viel klarer sein, wenn es nicht das Wort 'dies' hätte und anstelle des allgemeineren Qualifikators auf Konstanz Bezug nehmen würde .
Karneval
7

Einige neuere Versionen der GCC-Header (4.1 und 4.2 auf meinem Computer) haben nicht standardmäßige Elementfunktionen map :: at (), die als const deklariert sind und std :: out_of_range auslösen, wenn sich der Schlüssel nicht in der Map befindet.

const mapped_type& at(const key_type& __k) const

Aus einem Verweis im Kommentar der Funktion geht hervor, dass dies als neue Elementfunktion in der Standardbibliothek vorgeschlagen wurde.

Nathan Kitchen
quelle
Ich denke, das ist eine kleine Eigenart. Die at-Funktion ist Teil des kommenden Standards, aber ich finde kein at () im aktuellen.
Sebastian Mach
'at' ist Teil von C ++ 11.
Étienne
0

Erstens sollten Sie keine Symbole verwenden, die mit _ beginnen, da sie der Sprachimplementierung / dem Compiler-Writer vorbehalten sind. Es wäre sehr einfach für _map, ein Syntaxfehler für den Compiler einer anderen Person zu sein, und Sie hätten nur sich selbst die Schuld zu geben.

Wenn Sie einen Unterstrich verwenden möchten, setzen Sie ihn am Ende und nicht am Anfang. Sie haben diesen Fehler wahrscheinlich gemacht, weil Sie Microsoft-Code dabei gesehen haben. Denken Sie daran, dass sie ihren eigenen Compiler schreiben, damit sie möglicherweise damit durchkommen können. Trotzdem ist es eine schlechte Idee.

Der Operator [] gibt nicht nur eine Referenz zurück, sondern erstellt tatsächlich den Eintrag in der Karte. Sie erhalten also nicht nur ein Mapping, wenn es keines gibt, erstellen Sie eines. Das haben Sie nicht beabsichtigt.

Dov
quelle
5
Ihr Standpunkt _ist einfach falsch. Bezeichner, die mit zwei Unterstrichen ( __example) beginnen, oder Bezeichner, die mit einem Unterstrich und einem Großbuchstaben ( _Example) beginnen, sind reserviert. _exampleist nicht reserviert.
Ethan