Доступ к карте C ++ отбрасывает квалификаторы (const)

113

Следующий код говорит, что передача карты as constв operator[]метод отбрасывает квалификаторы:

#include <iostream>
#include <map>
#include <string>

using namespace std;

class MapWrapper {
public:
    const int &get_value(const int &key) const {
        return _map[key];
    }

private:
    map<int, int> _map;
};

int main() {
    MapWrapper mw;
    cout << mw.get_value(42) << endl;
    return 0;
}

Это из-за возможного выделения, которое происходит при доступе к карте? Нельзя ли объявить функции с доступом к карте как const?

MapWrapper.cpp:10: error: passing ‘const std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >’ as ‘this’ argument of ‘_Tp& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const _Key&) [with _Key = int, _Tp = int, _Compare = std::less<int>, _Alloc = std::allocator<std::pair<const int, int> >]’ discards qualifiers

cdleary
источник
Просто придирка, но mw можно объявить просто как MapWrapper mw;
люк
Хороший момент - я пишу на нескольких языках, поэтому стараюсь нормализовать синтаксис для них, чтобы все они умещались в моей голове. :)
cdleary 04
Я могу это оценить. Однако будьте осторожны, в таких случаях, как этот, у вас есть дополнительная конструкция и назначение объекта, в которых нет необходимости.
люк
Еще один хороший момент - использование оператора присваивания по умолчанию не является хорошей практикой для общедоступных примеров. ;)
cdleary 04

Ответы:

152

std::map's operator []не объявлен как constи не может быть обусловлен его поведением:

T & оператор [] (const Key & key)

Возвращает ссылку на значение, которое сопоставлено с ключом, эквивалентным ключу, выполняя вставку, если такой ключ еще не существует.

В результате ваша функция не может быть объявлена constи использовать карту operator[].

std::map«Sfind() функция позволяет искать ключ , не изменяя карту.

find()возвращает объект iteratorили const_iteratorобъект, std::pairсодержащий как ключ ( .first), так и значение ( .second).

В C ++ 11 вы также можете использовать at()для std::map. Если элемент не существует, функция выдает std::out_of_rangeисключение, в отличие от operator [].

Люк
источник
8
дополнительно: VALUE = map.find (KEY) -> второй; Мне пришлось узнать, что find () возвращает итератор типа pair.
FlipMcF
5
Я бы добавил, что теперь в C11 вы можете использовать: std :: map :: at (key) и избегать итератора.
Хуан Беса
3
Интересный. Я бы подумал, что C ++ будет различать lvalue operator[](например foo[bar] = baz) и rvalue operator[](например x = foo[bar]) - последнее, безусловно, может быть const.
Claudiu
15

Поскольку у operator[]него нет перегрузки с квалификацией const, его нельзя безопасно использовать в функции с квалификацией const. Вероятно, это связано с тем, что текущая перегрузка была создана с целью как возврата, так и установки значений ключей.

Вместо этого вы можете использовать:

VALUE = map.find(KEY)->second;

или, в C ++ 11, вы можете использовать at()оператор:

VALUE = map.at(KEY);
Ричард
источник
map.find(KEY)->second; небезопасно, если значения карты являются строками. Когда KEY не найден, он печатает мусор.
syam 08
1
Это правильно объясненный ответ, который идет по существу. Вчера я потратил 2 часа, пытаясь понять, что происходит с подобным случаем. Можем ли мы согласиться с тем, что сообщение об ошибке в лучшем случае вводит в заблуждение? Я мог бы быть более ясным, если бы в нем не было слова «это» и было бы указано на константу вместо более общего квалификатора .
carnicer
11

Вы не можете использовать operator[]на карте, constпоскольку этот метод не является, constпоскольку он позволяет вам изменять карту (вы можете назначить _map[key]). findВместо этого попробуйте использовать этот метод.

Нлативы
источник
1
В качестве пояснения: что должен делать оператор map [], если ключ не существует? Если карта не является константой, ключ добавляется со значением, созданным по умолчанию. Если карта const, что может вернуть оператор []? В этом ключе нет значения.
Это правильно объясненный ответ, который идет по существу. Вчера я потратил 2 часа, пытаясь понять, что происходит с подобным случаем. Можем ли мы согласиться с тем, что сообщение об ошибке в лучшем случае вводит в заблуждение? Я мог бы быть более ясным, если бы в нем не было слова «это» и было бы указано на константу вместо более общего квалификатора .
carnicer
7

Некоторые более новые версии заголовков GCC (4.1 и 4.2 на моей машине) имеют нестандартные функции-члены map :: at (), которые объявлены как const и выбрасывают std :: out_of_range, если ключ отсутствует на карте.

const mapped_type& at(const key_type& __k) const

Из ссылки в комментарии к функции видно, что это было предложено в качестве новой функции-члена в стандартной библиотеке.

Натан Китчен
источник
Думаю, это небольшая причуда. Функция at является частью будущего стандарта, но я не нахожу at () в текущем.
Себастьян Мах
'at' является частью C ++ 11.
Этьен,
0

Во-первых, вы не должны использовать символы, начинающиеся с _, потому что они зарезервированы для реализации языка / средства записи компилятора. Было бы очень легко, если бы _map стал синтаксической ошибкой в ​​чьем-то компиляторе, и вам было бы некого винить, кроме себя.

Если вы хотите использовать подчеркивание, ставьте его в конце, а не в начале. Вы, вероятно, сделали эту ошибку, потому что видели, как это делает код Microsoft. Помните, что они пишут свой собственный компилятор, так что им это может сойти с рук. Тем не менее, это плохая идея.

оператор [] не только возвращает ссылку, он фактически создает запись на карте. Таким образом, вы не просто получаете отображение, если его нет, вы его создаете. Это не то, что вы хотели.

Дов
источник
5
Ваша точка зрения _просто неверна. Идентификаторы, начинающиеся с двух подчеркиваний ( __example) или идентификаторы, начинающиеся с одного подчеркивания и заглавной буквы ( _Example), зарезервированы. _exampleне зарезервировано.
Итан