Почему повторное использование возвращенного std :: map.find итератора для стертой записи дает правильный ключ и случайное значение? - PullRequest
0 голосов
/ 01 апреля 2020

Я столкнулся с проблемой в своем коде, где я использовал следующую последовательность методов отображения find(k), erase(k), find(k) и оба раза обращался к возвращенным итераторам find первым и вторым членам.

Это заставило меня проверить правильно против map.find(k) == map.end(). Но что происходит за кулисами, если это не указано?

Даже если сравнение возвращаемого итератора find с .end() оценивается как true, it.first (старый ключ) и it.second (случайное значение) все еще доступно.

Версия компилятора: g ++ (Ubuntu 8.4.0-1ubuntu1 ~ 18.04) 8.4.0

Минимальный рабочий пример

#include <stdio.h>
#include <map>

int main(int argc, char const *argv[])
{
  std::map<size_t, size_t> test_map;
  test_map.insert(std::pair<size_t, size_t>(1,2));
  test_map.insert(std::pair<size_t, size_t>(2,1));

  printf("map<size_t, size_t> with %lu key-value pairs: (1,2) and (2,1). 
    Iterating over map:\n", test_map.size());

  for (const auto &it : test_map)
  {
    printf("entry found - k: %lu, v: %lu\n", it.first, it.second);
  }

  size_t a = test_map.find(1)->first;
  size_t b = test_map.find(1)->second;
  printf("Called map.find(1): entry found - k: %lu, v: %lu\n", a, b);

  test_map.erase(1);
  printf("Called map.erase(1)\n");

  if(test_map.find(1) == test_map.end())
    printf("map.find(1) == test_map.end()\n");

  if(test_map.find(2) == test_map.end())
    printf("map.find(2) == test_map.end()\n");

  size_t c = test_map.find(1)->first;
  size_t d = test_map.find(1)->second;
  printf("Called map.find(1): entry found - k: %lu, v: %lu\n", c, d);

  printf("Iterating over map again:\n");
  for (const auto &it : test_map)
  {
    printf("entry found - k: %lu, v: %lu\n", it.first, it.second);
  }

  return 0;
}

Выход

./map_test 
map<size_t, size_t> with 2 key-value pairs: (1,2) and (2,1). Iterating over map:
entry found - k: 1, v: 2
entry found - k: 2, v: 1
Called map.find(1): entry found - k: 1, v: 2
Called map.erase(1)
map.find(1) == test_map.end()
Called map.find(1): entry found - k: 1, v: 94380092077776 // k stays, v random
Iterating over map again:
entry found - k: 2, v: 1

1 Ответ

1 голос
/ 02 апреля 2020

Согласно документации std::map::end() (выделено мной):

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

Этот элемент действует как заполнитель; попытка доступа к нему приводит к неопределенному поведению .

Применительно к Неопределенное поведение :

Компиляторы не требуются диагностировать неопределенное поведение (хотя диагностируется много простых ситуаций), и скомпилированная программа не обязана делать что-либо значимое.

Итак, это то, что вы видите. Поведение определяется реализацией.


В вашем конкретном случае std::map, std::map::find() возвращает итератор past-the-last-element, а std::map::end() возвращает то же самое; следовательно, их сравнение для тестирования.

Итератор элемента last-the-last или std::map::end() возвращает действительный итератор, но он не является разыменованным . См .: std::map::erase().

Применительно к правилам Inratoridation для ассоциативных контейнеров все итераторы действительны, кроме итераторов / ссылок на стертый элемент ( s).

Прочтите этот SO thread , чтобы получить сводную информацию об отмене итерации (включая ссылки на соответствующие разделы стандартов C ++).

...