Почему оператор [] не является константой для карт STL?

Надуманный пример, ради вопроса:

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

Это не будет компилироваться, поскольку оператор [] не является константой.

Это прискорбно, поскольку синтаксис [] выглядит очень чистым. Вместо этого я должен сделать что-то вроде этого:

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

Меня это всегда беспокоило. Почему оператор [] не является константой?


person Runcible    schedule 25.09.2009    source источник
comment
Что должно operator[] дать, если данный элемент не существует?   -  person Frerich Raabe    schedule 16.11.2011
comment
@Frerich Raabe: То же, что и функция-член at: throw std :: out_of_range   -  person Jean-Simon Brochu    schedule 08.08.2017


Ответы (6)


Для std::map и std::unordered_map operator[] вставит значение индекса в контейнер, если оно ранее не существовало. Это немного не интуитивно, но так оно и есть.

Поскольку необходимо разрешить сбой и вставить значение по умолчанию, оператор не может использоваться в const экземпляре контейнера.

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

person Alan    schedule 25.09.2009
comment
std::set не имеет operator[]. - person avakar; 25.09.2009
comment
Это правильный ответ, но константная версия может делать то же самое, что и член at. То есть бросить std :: out_of_range ... - person Jean-Simon Brochu; 08.08.2017
comment
При использовании для чтения значения значение по умолчанию не предоставляется. std::vector имеет оператор чтения [], то есть const. map должен поступить так же. - person wcochran; 16.09.2020
comment
@ Jean-SimonBrochu соглашение во всех контейнерах гласит, что at может бросать, а [] нет. Я не думаю, что стоит нарушать это соглашение. - person Caleth; 25.03.2021

Теперь, когда с C ++ 11 вы можете получить более чистую версию, используя at ()

void MyClass::MyFunction( int x ) const
{
  std::cout << m_map.at(x) << std::endl;
}
person Deqing    schedule 09.08.2013
comment
Если map имеют константные и неконстантные at()s - почему не то же самое и для operator[]? с версией const ничего не вставляет, а бросает? (Или возврат необязательного, когда std :: optional делает его стандартом) - person einpoklum; 10.12.2015
comment
@einpoklum Смысл правильности констант - это в основном статическая проверка во время компиляции. Я предпочитаю, чтобы компилятор жаловался, чем генерировал исключение, потому что я неправильно использовал константные объекты. - person Millie Smith; 18.04.2016
comment
@einpoklum Очень поздно, но для других читателей: иметь две перегрузки, делающие такие разные вещи, было бы ужасно. Единственная причина, по которой at бывает двух видов, состоит в том, что он выполняет return *this;, а единственная разница между перегрузками - это const-размер возвращаемой ссылки. Фактические эффекты обоих at одинаковы (то есть никакого эффекта). - person HTNW; 20.04.2020

Примечание для новых читателей.
Изначально вопрос касался контейнеров STL (а не конкретно std :: map)

Следует отметить, что в большинстве контейнеров есть константная версия operator [].
Просто std :: map и std :: set не имеют константной версии, и это результат базовой структуры, которая реализует их.

Из std :: vector

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

Также для вашего второго примера вы должны проверить, не удается ли найти элемент.

void MyClass::MyFunction( int x ) const
{
    MyMap iter = m_map.find(x);
    if (iter != m_map.end())
    {
        std::cout << iter->second << std::endl
    }
}
person Martin York    schedule 25.09.2009
comment
std::set вообще не имеет operator[]. - person Everyone; 19.04.2017

Поскольку operator [] может вставлять новый элемент в контейнер, он не может быть константной функцией-членом. Обратите внимание, что определение operator [] чрезвычайно простое: m [k] эквивалентно (* ((m.insert (value_type (k, data_type ())). First)). Second. Строго говоря, в этой функции-члене нет необходимости: она существует только для удобства.

person Satbir    schedule 25.09.2009

Оператор индекса должен быть константным только для контейнера, доступного только для чтения (который на самом деле не существует в STL как таковой).

Операторы индекса используются не только для просмотра значений.

person Nick Bedford    schedule 25.09.2009
comment
Вопрос в том, почему у него нет двух перегруженных версий - одной const, другой не const - например, std::vector делает. - person Pavel Minaev; 25.09.2009

Если вы объявляете переменную члена std :: map изменяемой

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

вы можете использовать неконстантные функции-члены std :: map в своих константных функциях-членах.

person Anthony Cramp    schedule 25.09.2009
comment
Однако это ужасная идея. - person GManNickG; 26.09.2009
comment
Если вы это сделаете, API вашего класса окажется ложным. Функция утверждает, что она является константой, что означает, что она не будет изменять никакие переменные-члены, но на самом деле она может изменять член данных m_map. - person Runcible; 18.03.2010
comment
mutable может использоваться для таких членов, как std::mutex, кеши и помощники отладки. Если карта должна использоваться в качестве кеша для ускорения очень дорогой const функции получения, тогда mutable приемлемо. Вам нужно быть осторожным, но само по себе это неплохая идея. - person Mark Lakata; 29.09.2015