Элемент карты С++ не стирается, если я на него ссылаюсь

Играя с классом std::map стандартной библиотеки С++, я заметил, что если я удалю элемент, а затем попытаюсь сослаться на него (закомментированная строка в коде ниже), элемент вернется со значением 0. Ожидается ли это ? Вам действительно нужно использовать функцию поиска для доступа к элементу, не создавая его случайно, если он не существует?

настройка компилятора: я компилирую на OSX 10.8.3 с помощью g++ i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1 (на основе Apple Inc. build 5658) (LLVM build 2336.11.00)

using namespace std;

map<int,int> myMap;
map<int,int>::iterator it;

myMap[1] = 5;

for (it=myMap.begin(); it!=myMap.end(); ++it)
    std::cout << it->first << " => " << it->second << '\n';

printf("map test result: %d\n", myMap[1]);

it = myMap.find(1);
myMap.erase( it );

// If I uncomment this it will cause a 0 value to occur at key 1.
//printf("map test result: %d\n", myMap[1]);

if (myMap.find(1) == myMap.end())
    puts("element key 1 is null, ok");
else
    puts("element @ key 1 exists, error");

if (myMap.empty())
    puts("map is empty");
else
    puts("map is not empty, error");

for (it=myMap.begin(); it!=myMap.end(); ++it)
    std::cout << it->first << " => " << it->second << '\n';

Просто чтобы уточнить, если я запускаю это со второй прокомментированной строкой printf, она работает, как и ожидалось:

1 => 5
map test result: 5
element key 1 is null, ok
map is empty

Если я запускаю строку без комментариев, доступ к myMap[1] в операторе printf создает еще один элемент и оставляет такой результат:

1 => 5
map test result: 5
map test result: 0
element @ key 1 exists, error
map is not empty, error
1 => 0

person Vivek Gani    schedule 11.06.2013    source источник


Ответы (2)


Да, это то, что должен делать operator[] из std::map. Из стандарта (С++ 11, §23.5.4.3):

mapped_type& operator[](const key_type& k);
mapped_type& operator[](key_type&& k);

[...]

Эффекты: Если unordered_map еще не содержит элемент, ключ которого эквивалентен k, первый оператор вставляет значение value_type(k, mapped_type()), а второй оператор вставляет значение value_type(std::move(k), mapped_type()).

Обратите внимание, что это произойдет, даже если элемент никогда не был вставлен, а затем стерт. Доступ к элементам с использованием operator[] просто вставляет новые, созданные по умолчанию значения, когда вы применяете их к несуществующим ключам.

Если вы этого не хотите, лучше всего используйте функцию find из std::map. Это вернет end-итератор, если ключ не существует.

person jogojapan    schedule 11.06.2013

Да, это ожидаемое поведение. Помимо чтения спецификации, вы можете сделать вывод об этом из сигнатуры типа:

T& operator[] (const key_type& k);

Он не может сказать, закончите ли вы присваивание ключу, потому что operator = вызывается для возвращаемого значения после того, как оператор [] уже завершил выполнение. Метод также не может представлять пустое значение: он возвращает ссылку, а не указатель.

person Wilbur Vandrsmith    schedule 11.06.2013
comment
+1 (хотя теоретически карта могла вернуть ссылку на какой-то фиктивный элемент. Странный дизайн, но не исключено). - person jogojapan; 11.06.2013
comment
То, как это работает сейчас, делает его идеальным для счетчиков или карт контейнеров. map[key]++ или map[key].push_back(value) намного чище, чем эквивалент в языках, которые этого не делают (например, java) - person Bwmat; 11.06.2013
comment
@jogojapan Действительно! Конечно, это можно проверить, посмотрев, является ли T тем же T, что вы указали в std::map<T>. - person Wilbur Vandrsmith; 11.06.2013