Warum ist der Operator [] nicht konstant für STL-Karten?

87

Erfundenes Beispiel für die Frage:

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map[x] << std::endl
}

Dies wird nicht kompiliert, da der Operator [] nicht const ist.

Dies ist bedauerlich, da die Syntax [] sehr sauber aussieht. Stattdessen muss ich so etwas tun:

void MyClass::MyFunction( int x ) const
{
  MyMap iter = m_map.find(x);
  std::cout << iter->second << std::endl
}

Das hat mich immer nervt. Warum ist der Operator [] nicht konstant?

Runcible
quelle
5
Was sollte sich operator[]ergeben, wenn das angegebene Element nicht existiert?
Frerich Raabe
3
@ Frerich Raabe: Das gleiche wie die at member Funktion: throw std :: out_of_range
Jean-Simon Brochu

Antworten:

88

Für std::mapund std::unordered_map, operator[]den Indexwert in den Behälter einfügen , wenn es zuvor nicht vorhanden ist. Es ist ein wenig unintuitiv, aber so ist es.

Da es fehlschlagen muss, einen Standardwert einzufügen, kann der Operator nicht für eine constInstanz des Containers verwendet werden.

http://en.cppreference.com/w/cpp/container/map/operator_at

Alan
quelle
3
std::sethat nicht operator[].
Avakar
1
Das ist die richtige Antwort, aber eine const-Version könnte dasselbe tun wie das "at" -Mitglied. Das ist ein std :: out_of_range werfen ...
Jean-Simon Brochu
49

Jetzt, da Sie mit C ++ 11 eine sauberere Version haben können, verwenden Sie at ()

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}
Deqing
quelle
4
Wenn mapconst und non-const at()s haben - warum nicht auch für operator[]? mit der const version nichts einfügen sondern werfen? (Oder ein optionales zurückgeben, wenn std :: optional es in den Standard
schafft
@einpoklum Der Punkt der Konstantenkorrektheit ist meistens die statische Überprüfung der Kompilierungszeit. Ich möchte mich lieber beim Compiler beschweren, als eine Ausnahme auszulösen, da ich const-Objekte nicht richtig verwendet habe.
Millie Smith
@einpoklum Sehr spät, aber für andere Leser: Es wäre schrecklich, wenn zwei Überladungen so unterschiedliche Dinge tun würden. Der einzige Grund, den es atin zwei Varianten gibt, ist, dass es a tut return *this;, und der einzige Unterschied zwischen den Überladungen ist die const-ness der zurückgegebenen Referenz. Die tatsächlichen Auswirkungen beider ats sind genau gleich (dh keine Auswirkung).
HTNW
28

Hinweis für neue Leser.
Die ursprüngliche Frage betraf STL-Container (nicht speziell die std :: map)

Es sollte beachtet werden, dass es auf den meisten Containern eine konstante Version von operator [] gibt.
Es ist nur so, dass std :: map und std :: set keine const-Version haben und dies ist ein Ergebnis der zugrunde liegenden Struktur, die sie implementiert.

Von std :: vector

reference       operator[](size_type n) 
const_reference operator[](size_type n) const 

Auch für Ihr zweites Beispiel sollten Sie prüfen, ob das Element nicht gefunden werden kann.

void MyClass::MyFunction( int x ) const
{
    MyMap iter = m_map.find(x);
    if (iter != m_map.end())
    {
        std::cout << iter->second << std::endl
    }
}
Martin York
quelle
1
std::sethat überhaupt nicht operator[].
Jeder
2

Da operator [] möglicherweise ein neues Element in den Container einfügt, kann es möglicherweise keine const-Member-Funktion sein. Beachten Sie, dass die Definition von Operator [] äußerst einfach ist: m [k] entspricht (* ((m.insert (value_type (k, data_type ()))). First)). Second. Genau genommen ist diese Mitgliedsfunktion nicht erforderlich: Sie existiert nur zur Vereinfachung

Satbir
quelle
0

Ein Indexoperator sollte nur für einen schreibgeschützten Container (der in STL per se nicht wirklich vorhanden ist) const sein.

Indexoperatoren werden nicht nur zum Anzeigen von Werten verwendet.

Nick Bedford
quelle
5
Die Frage ist, warum es nicht zwei überladene Versionen gibt - eine const, eine andere const- wie z std::vector.
Pavel Minaev
-2

Wenn Sie Ihre std :: map-Mitgliedsvariable als veränderbar deklarieren

mutable std::map<...> m_map;

Sie können die Nicht-Const-Member-Funktionen von std :: map in Ihren Const-Member-Funktionen verwenden.

Anthony Cramp
quelle
15
Dies ist jedoch eine schreckliche Idee.
GManNickG
7
Die API für Ihre Klasse liegt, wenn Sie das tun. Die Funktion behauptet, dass es const ist - was bedeutet, dass keine Mitgliedsvariablen geändert werden -, aber in Wirklichkeit könnte es das m_map-Datenelement ändern.
Runcible
2
mutablekann für Mitglieder wie std::mutexCaches und Debug-Helfer verwendet werden. Wenn die Karte als Cache verwendet werden soll, um eine sehr teure const"Getter" -Funktion zu beschleunigen , mutableist dies akzeptabel. Sie müssen vorsichtig sein, aber es ist keine schreckliche Idee für sich.
Mark Lakata