C ++ map Access odrzuca kwalifikatory (const)

113

Poniższy kod mówi, że przekazanie mapy as constdo operator[]metody powoduje odrzucenie kwalifikatorów:

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

Czy to z powodu możliwej alokacji, która występuje na mapie dostępu? Czy żadne funkcje z dostępem do mapy nie mogą być deklarowane jako stałe?

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
źródło
Tylko szczypta, ale mw można zadeklarować po prostu jako mw MapWrapper;
Łukasz
Słuszna uwaga - piszę w kilku językach, więc staram się normalizować składnię w nich, aby wszystkie pasowały do ​​mojej głowy. :)
cdleary
Potrafię to docenić. Uważaj jednak, w przypadkach takich jak ten masz dodatkową konstrukcję obiektu i przypisanie, które nie są konieczne.
Łukasz
Kolejna dobra uwaga - poleganie na domyślnym operatorze przypisania nie jest dobrą praktyką w przypadku przykładów publicznych. ;)
cdleary

Odpowiedzi:

152

std::map„s operator []nie jest zadeklarowana jako const, i nie może być ze względu na jego zachowanie:

T & operator [] (klucz i klucz const)

Zwraca odwołanie do wartości, która jest zamapowana na klucz odpowiadający kluczowi, wykonując wstawianie, jeśli taki klucz jeszcze nie istnieje.

W rezultacie Twoja funkcja nie może zostać zadeklarowana consti użyj funkcji mapy operator[].

std::map„sfind() funkcja pozwala zajrzeć do klucza bez modyfikowania map.

find()zwraca iteratorlub const_iteratordo std::pairzawierającego zarówno klucz ( .first), jak i wartość ( .second).

W C ++ 11 można również użyć at()for std::map. Jeśli element nie istnieje, funkcja zgłasza std::out_of_rangewyjątek, w przeciwieństwie do operator [].

Łukasz
źródło
8
dodatkowo: VALUE = map.find (KEY) -> sekunda; Musiałem się nauczyć, że funkcja „find ()” zwraca iterator, który jest parą typów.
FlipMcF,
5
Dodałbym, że teraz w C11 można użyć: std :: map :: at (key) i uniknąć iteratora.
Juan Besa,
3
Ciekawy. Myślę, że C ++ rozróżniłoby między lvalue operator[](np. foo[bar] = baz) I rvalue operator[](np. x = foo[bar]) - ta ostatnia z pewnością mogłaby być const.
Claudiu
15

Ponieważ operator[]nie ma przeciążenia z kwalifikacją stałą, nie można go bezpiecznie używać w funkcji z kwalifikacją stałą. Dzieje się tak prawdopodobnie dlatego, że bieżące przeciążenie zostało utworzone w celu zarówno zwrócenia, jak i ustawienia wartości kluczowych.

Zamiast tego możesz użyć:

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

lub w C ++ 11 możesz użyć at()operatora:

VALUE = map.at(KEY);
Richard
źródło
map.find(KEY)->second; nie jest bezpieczny, gdy wartości mapy są ciągami. Ma tendencję do drukowania śmieci, gdy KEY nie zostanie znaleziony.
syam
1
To właściwie wyjaśniona odpowiedź, która prowadzi do sedna. Wczoraj spędziłem 2 godziny, próbując dowiedzieć się, co się dzieje z podobnym przypadkiem. Czy możemy się zgodzić, że komunikat o błędzie jest w najlepszym przypadku mylący? Mógłbym być o wiele bardziej zrozumiały, gdyby nie zawierało słowa „to” i zawierało odniesienie do const-ness zamiast do bardziej ogólnego kwalifikatora .
karniker
11

Nie możesz używać operator[]na mapie, constponieważ ta metoda nie jest, constponieważ umożliwia modyfikowanie mapy (możesz przypisać do _map[key]). Spróbuj findzamiast tego użyć tej metody.

nlativy
źródło
1
Tytułem wyjaśnienia: co powinien zrobić operator map [], jeśli klucz nie istnieje? Jeśli mapa nie jest stała, klucz jest dodawany z domyślnie skonstruowaną wartością. Jeśli stała mapowania, co może zwrócić operator []? W tym kluczu nie ma wartości.
To właściwie wyjaśniona odpowiedź, która prowadzi do sedna. Wczoraj spędziłem 2 godziny, próbując dowiedzieć się, co się dzieje z podobnym przypadkiem. Czy możemy się zgodzić, że komunikat o błędzie jest w najlepszym przypadku mylący? Mógłbym być o wiele bardziej zrozumiały, gdyby nie zawierało słowa „to” i zawierało odniesienie do const-ness zamiast do bardziej ogólnego kwalifikatora .
karniker
7

Niektóre nowsze wersje nagłówków GCC (4.1 i 4.2 na moim komputerze) mają niestandardowe funkcje składowe map :: at (), które są zadeklarowane jako const i generują std :: out_of_range, jeśli klucza nie ma na mapie.

const mapped_type& at(const key_type& __k) const

Z odniesienia w komentarzu do funkcji wynika, że ​​zasugerowano to jako nową funkcję składową w bibliotece standardowej.

Nathan Kitchen
źródło
Myślę, że to trochę dziwaczne. Funkcja at jest częścią nadchodzącego standardu, ale nie znajduję at () w obecnym.
Sebastian Mach,
„at” jest częścią C ++ 11.
Étienne
0

Po pierwsze, nie powinieneś używać symboli zaczynających się od _, ponieważ są one zarezerwowane dla implementacji języka / programu zapisującego kompilator. Byłoby bardzo łatwo, gdyby _map był błędem składniowym w czyimś kompilatorze i nie można by obwiniać nikogo poza sobą.

Jeśli chcesz użyć podkreślenia, umieść go na końcu, a nie na początku. Prawdopodobnie popełniłeś ten błąd, ponieważ widziałeś, jak robi to jakiś kod Microsoft. Pamiętaj, że piszą własny kompilator, więc może im się to udać. Mimo wszystko to zły pomysł.

operator [] nie tylko zwraca odniesienie, ale w rzeczywistości tworzy wpis na mapie. Więc nie otrzymujesz tylko mapowania, jeśli nie ma, tworzysz je. Nie tego chciałeś.

Dov
źródło
5
Twój punkt widzenia _jest po prostu błędny. Identyfikatory rozpoczynające się od dwóch podkreślników ( __example) lub identyfikatorów rozpoczynających się od jednego podkreślenia i dużej litery ( _Example) są zastrzeżone. _examplenie jest zastrzeżone.
Ethan