Почему оператор [] не является константой для карт 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;
}

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это приемлемо. Вам нужно быть осторожным, но само по себе это неплохая идея.
Марк Лаката
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.