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

89

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

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
}

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

Работоспособен
источник
5
Что должно operator[]дать, если данного элемента не существует?
Frerich Raabe
4
@Frerich Raabe: То же, что и функция-член at: throw std :: out_of_range
Жан-Симон Брошу,

Ответы:

90

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

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

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

Алан
источник
3
std::setне имеет operator[].
avakar
2
Это правильный ответ, но константная версия может делать то же самое, что и член «at». Это бросить std :: out_of_range ...
Жан-Симон Брошу
При использовании для чтения значения значение по умолчанию не предоставляется. std::vectorимеет оператор чтения, []то есть const. mapдолжен делать то же самое.
wcochran
51

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

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

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

Следует отметить, что в большинстве контейнеров есть константная версия оператора [].
Просто 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
    }
}
Мартин Йорк
источник
1
std::setсовсем нет operator[].
Все
2

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

Сатбир
источник
0

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

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

Ник Бедфорд
источник
6
Вопрос в том, почему у него нет двух перегруженных версий - одной constи другой const- как, например, std::vectorесть.
Павел Минаев
-2

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

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

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

Энтони Крэмп
источник
15
Однако это ужасная идея.
GManNickG
7
Если вы это сделаете, API вашего класса окажется ложным. Функция утверждает, что она является константой, что означает, что она не будет изменять никакие переменные-члены, но на самом деле она может изменять член данных m_map.
Runcible
2
mutableможет использоваться для таких членов, как std::mutexкеши и помощники отладки. Если карта будет использоваться в качестве кеша для ускорения очень дорогогоconst «получения», то mutableэто приемлемо. Вам нужно быть осторожным, но само по себе это неплохая идея.
Марк Лаката