Чому оператор [] не є const для карт STL?


89

Надуманий приклад для питання:

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

Це не компілюється, оскільки оператор [] не є const.

Це прикро, оскільки синтаксис [] виглядає дуже чисто. Натомість я маю зробити щось подібне:

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

Це мене завжди хвилювало. Чому оператор [] не є const?


5
Що має operator[]дати в разі, якщо даний елемент не існує?
— Frerich Raabe

4
@Frerich Raabe: Те саме, що і функція at member: 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[].
— авакар

2
Це правильна відповідь, але версія const може робити те саме, що і член "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 і non-const at()s - чому б не однаково і для operator[]? при цьому версія const нічого не вставляє, а швидше кидає? (Або повернення необов’язкового, коли std :: optional робить його стандартним)
— einpoklum

@einpoklum Точкою правильності const є переважно статична перевірка часу компіляції. Я вважаю за краще, щоб компілятор скаржився, ніж викидав виняток, оскільки я неправильно використовував об’єкти const.
— Millie Smith

@einpoklum Дуже пізно, але для інших читачів: мати два перевантаження, які роблять такі різні речі, було б жахливо. Єдина причина atв двох варіантах полягає в тому, що це робить a return *this;, і єдина різниця між перевантаженнями полягає у const-ness повернутого посилання. Фактичні ефекти обох ats абсолютно однакові (тобто відсутність ефекту).
— HTNW

28

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

Слід зазначити, що для більшості контейнерів існує const-версія оператора [].
Просто std :: map і std :: set не мають const-версії, і це результат базової структури, яка їх реалізує.

Від 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

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


0

Оператор індексу повинен бути const лише для контейнера, доступного лише для читання (який насправді не існує в STL).

Оператори індексу використовуються не лише для перегляду значень.


6
Питання в тому, чому у нього немає двох перевантажених версій - одна const, інша не-, constяк, наприклад std::vector, є.
— Павло Мінаєв

-2

Якщо ви оголосите свою змінну члена std :: map змінною

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

Ви можете використовувати функції не-const-члена std :: map у своїх функціях-членах const.


15
Однак це жахлива ідея.
— GManNickG

7
Якщо ви це зробите, API для вашого класу лежить. Функція стверджує, що вона const - це означає, що вона не змінить жодних змінних-членів, - але насправді це може бути зміна елемента даних m_map.
— Runcible

2
mutableможе використовуватися для таких членів, як std::mutexкеші та помічники налагодження. Якщо карту слід використовувати як кеш для прискорення дуже дорогої функції const"отримання", тоді mutableце прийнятно. Потрібно бути обережним, але це не страшна ідея сама по собі.
— Mark Lakata
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.