Dlaczego nie jest stała operatora [] dla map STL?

89

Wymyślony przykład, aby odpowiedzieć na pytanie:

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

To się nie skompiluje, ponieważ operator [] nie jest stałą.

Jest to niefortunne, ponieważ składnia [] wygląda bardzo czysto. Zamiast tego muszę zrobić coś takiego:

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

To zawsze mnie wkurza. Dlaczego operator [] nie jest stałą?

Runcible
źródło
5
Co powinno operator[]dać, gdy dany element nie istnieje?
Frerich Raabe
4
@Frerich Raabe: To samo, co funkcja członka at: throw std :: out_of_range
Jean-Simon Brochu

Odpowiedzi:

90

For std::mapand std::unordered_map, operator[]wstawi wartość indeksu do kontenera, jeśli wcześniej nie istniała. To trochę nieintuicyjne, ale tak właśnie jest.

Ponieważ musi mieć możliwość niepowodzenia i wstawienia wartości domyślnej, operatora nie można używać na constwystąpieniu kontenera.

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

Alan
źródło
3
std::setnie ma operator[].
avakar
2
To jest właściwa odpowiedź, ale wersja const może zrobić to samo, co element „at”. To jest rzut std :: out_of_range ...
Jean-Simon Brochu
Gdy jest używany do odczytu wartości, nie ma wartości domyślnej do podania. std::vectorma odczytu operatora [], który jest const. mappowinien zrobić to samo.
wcochran
51

Teraz z C ++ 11 możesz mieć czystszą wersję używając at ()

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}
Deqing
źródło
4
Jeśli mapmasz const i non-const at()s - dlaczego nie to samo również dla operator[]? z wersją const nic nie wstawia, tylko rzuca? (Lub zwracanie opcjonalnego, gdy std :: optional robi to w standardzie)
einpoklum
@einpoklum Punktem poprawności const jest głównie statyczne sprawdzanie czasu kompilacji. Wolałbym, żeby kompilator narzekał niż rzucał wyjątek, ponieważ nie użyłem poprawnie obiektów const.
Millie Smith
@einpoklum Bardzo późno, ale dla innych czytelników: posiadanie dwóch przeciążeń wykonujących tak różne rzeczy byłoby okropne. Jedynym powodem, dla którego atwystępuje w dwóch odmianach, jest to, że robi a return *this;, a jedyną różnicą między przeciążeniami jest const-ness zwróconego odwołania. Rzeczywiste skutki obu efektów atsą dokładnie takie same (to znaczy brak efektu).
HTNW
28

Uwaga dla nowych czytelników.
Pierwotne pytanie dotyczyło kontenerów STL (a nie konkretnie std :: map)

Należy zauważyć, że na większości kontenerów istnieje wersja const operatora [].
Po prostu std :: map i std :: set nie mają wersji const i jest to wynikiem podstawowej struktury, która je implementuje.

Z std :: vector

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

Również w drugim przykładzie powinieneś sprawdzić, czy nie udało się znaleźć elementu.

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
źródło
1
std::setw ogóle nie ma operator[].
Wszyscy
2

Ponieważ operator [] może wstawić nowy element do kontenera, nie może to być stała funkcja składowa. Zauważ, że definicja operatora [] jest niezwykle prosta: m [k] jest równoważne z (* ((m.wstaw (typ_wartości (k, typ_danych ())). Pierwsza)). Sekunda. Ściśle mówiąc, ta funkcja członkowska jest niepotrzebna: istnieje tylko dla wygody

Satbir
źródło
0

Operator indeksu powinien być stały tylko dla kontenera tylko do odczytu (który tak naprawdę nie istnieje w STL jako taki).

Operatory indeksu służą nie tylko do sprawdzania wartości.

Nick Bedford
źródło
6
Pytanie brzmi, dlaczego nie ma dwóch przeciążonych wersji - jednej const, drugiej nie const- jak np std::vector.
Pavel Minaev
-2

Jeśli zadeklarujesz zmienną składową std :: map jako mutowalną

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

możesz użyć funkcji składowych niebędących stałymi std :: map w ramach funkcji składowych stałych.

Anthony Cramp
źródło
15
To jednak okropny pomysł.
GManNickG
7
API dla twojej klasy kłamie, jeśli to zrobisz. Funkcja twierdzi, że jest stała - co oznacza, że ​​nie modyfikuje żadnych zmiennych składowych - ale w rzeczywistości może modyfikować składową danych m_map.
Runcible,
2
mutablemoże być używany dla członków, takich jak std::mutexpamięci podręczne i pomocnicy debugowania. Jeśli mapa ma być używana jako pamięć podręczna w celu przyspieszenia bardzo kosztownej funkcji const„pobierającej”, to mutablejest to dopuszczalne. Trzeba być ostrożnym, ale sam w sobie nie jest to straszny pomysł.
Mark Lakata