Подтвердить что ты не робот

Когда я должен использовать std:: map:: at для извлечения элемента карты

Я читал разные статьи по веб-вопросам и вопросам на stackoverflow, но для меня неясно, есть ли какой-либо эксклюзивный случай, когда лучше использовать std::map::at для извлечения элемента карты.

В соответствии с определение, std::map::at

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

Если k не соответствует ключу любого элемента в контейнере, функция выбрасывает исключение out_of_range.

Для меня только случай, когда стоит использовать std::map::at, когда вы 100% уверены, что элемент с определенным ключом существует, иначе вы должны рассмотреть обработку исключений.

  • Есть ли случай, когда std::map::at считается наиболее эффективным и элегантным способом? В каких случаях вы рекомендуете использовать std::map::at?
  • Правильно ли, что лучше использовать map::find(), когда есть возможность не иметь элемент с таким ключом? И map::find() это более быстрый и элегантный подход?
if ( map.find("key") != map.end() )
{
    // found 

} else
{
    // not found
}

p.s

map::operator[] иногда может быть опасным, потому что, если элемент не существует, он будет его вставлять.

EDITED: ссылки как-то связаны ссылка 1 ссылка 2 ссылка 3 ссылка 4 ссылка 5 ссылка 6

4b9b3361

Ответ 1

В отличие от большинства существующих ответов здесь обратите внимание, что существуют фактически 4 методы, связанные с нахождением элемента на карте (игнорируя lower_bound, upper_bound и equal_range, которые менее точны ):

  • operator[] существует только в неконтинентной версии, так как отмечено, что он создаст элемент, если он не существует
  • at(), введенный в С++ 11, возвращает ссылку на элемент, если он существует, и выбрасывает исключение иначе
  • find() возвращает итератор элементу, если он существует, или единственный итератор, если он не
  • count() возвращает число таких элементов, в map, это 0 или 1

Теперь, когда семантика понятна, давайте рассмотрим, когда использовать:

  • если вы хотите узнать, присутствует ли элемент в map (или нет), используйте count().
  • если вы хотите получить доступ к элементу, и он должен находиться в map, затем используйте at().
  • если вы хотите получить доступ к элементу и не знаете, находится ли он в map или нет, используйте find(); не забудьте проверить, что полученный итератор не равен результату end().
  • наконец, если вы хотите получить доступ к элементу, если он существует, или создать его (и получить к нему), если это не так, используйте operator[]; если вы не хотите вызывать конструктор по умолчанию для его создания, используйте либо insert, либо emplace соответственно

Ответ 2

std::map::at() генерирует исключение out_of_range, если элемент не найден. Это исключение является своего рода исключением logic_error, которое для меня является своего рода синонимом assert() с точки зрения использования: оно должно использоваться для сообщения об ошибках во внутренней логике программы, таких как нарушение логических предпосылок или инвариантов классов.

Кроме того, вы можете использовать at() для доступа к картам const.

Итак, для ваших вопросов:

  • Я рекомендую использовать at() вместо [] при доступе к картам const и когда отсутствие элемента является логической ошибкой.
  • Да, лучше использовать map::find(), когда вы не уверены, что этот элемент здесь: в этом случае это не логическая ошибка, поэтому исключение и исключение std::logic_error не будет очень элегантным способом программирования, даже если мы не думаем о производительности.

Ответ 3

Как вы отметили, существует три способа доступа к элементам на карте: at(), operator[] и find() (есть также upper_bound, lower_bound и equal_range, но они предназначены для более сложные обстоятельства, при которых вы можете найти следующий/предыдущий элемент и т.д.)

Итак, когда вы должны использовать какой?

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

at() выдает, если для этого ключа нет элемента. Поскольку вы не должны использовать исключения для обычного потока программ, использование at() говорит "Я уверен, что есть такой элемент". Но с дополнительным преимуществом вы получаете исключение (а не поведение undefined), если вы ошибаетесь. Не используйте это, если вы не уверены, что элемент существует.

find() говорит: "может быть или не быть таким элементом, пусть видит..." и предлагает вам возможность реагировать на оба случая по-разному. Следовательно, это более общий подход.

Ответ 4

Полезны все 3 из find, operator[] и at.

  • find хорошо, если вы не хотите случайно вставлять элементы, а просто действуете, если они существуют.

  • at хорошо, если вы ожидаете, что что-то должно быть на карте, и вы выбросите исключение, если это не так. Он также может получить доступ к картам const в более сжатом виде, чем find (где вы не можете использовать op[])

  • op[] хорош, если вы хотите вставить элемент по умолчанию, например, для программы подсчета слов, которая помещает int 0 для каждого слова, встречающегося в первый раз (с idiom words[word]++;).

Ответ 5

Это зависит от требований к этой функции и того, как вы структурируете проект. Если вы должны вернуть объект, и вы не можете, потому что он не был найден, он оставляет вам два варианта, как их обрабатывать. Вы могли бы через исключение, или вы могли бы вернуть какой-то страж, что означает, что ничего не найдено. Если вы хотите выбросить исключение, используйте at(), поскольку исключение будет выбрано для вас. Если вы не хотите генерировать исключение, используйте find(), поэтому вам не нужно иметь дело с обработкой исключения, чтобы вернуть объект-дозор.

Ответ 6

Я думаю, это зависит от вашего usecase. Возвращаемый тип std::map::at() - это ссылка на значение найденного элемента, а std::map::find() возвращает итератор. Вы можете предпочесть

return myMap.at("asdf"s) + 42;

в выражениях над более сложными

return myMap.find("asdf"s)->second + 42;

Всякий раз, когда вы используете результат выражения std::map::at() в выражении, вы ожидаете существования элемента и считаете отсутствующий элемент как ошибку. Таким образом, исключение - хороший выбор для этого.

Ответ 7

Я думаю, что разница - это семантика.

std::map::at() выглядит так на моей машине:

mapped_type&
at(const key_type& __k)
{
    iterator __i = lower_bound(__k);
    if (__i == end() || key_comp()(__k, (*__i).first))
        __throw_out_of_range(__N("map::at"));
    return (*__i).second;
}

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

find() выглядит следующим образом:

iterator
find(const key_type& __x)
{ return _M_t.find(__x); }

где _M_t - это красно-черное дерево, в котором хранятся фактические данные. Очевидно, что обе функции имеют одинаковую (логарифмическую) сложность. Когда вы используете find() + check для end(), вы делаете почти то же самое, что и at. Я бы сказал, что семантическая разница:

  • используйте at(), когда вам нужен элемент в определенном месте, и вы предполагаете, что он есть. В этом случае ситуация с элементом, отсутствующим в нужном месте, является исключительной, поэтому at() генерирует исключение.
  • используйте find(), когда вам нужно найти элемент на карте. В этом случае ситуация, когда элемент отсутствует, является нормальным. Также обратите внимание, что find() возвращает итератор, который вы можете использовать для целей, отличных от простого получения его.

Ответ 8

map:: at() возвращает ссылку l-value, и когда вы возвращаетесь по ссылке, вы можете использовать все ее доступные преимущества, такие как цепочка методов.

Пример:

  map<int,typ> table;
  table[98]=a;
  table[99]=b;

  table.at(98)=table.at(99);

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

Это требует от вас дополнительной осторожности, поскольку вы должны заботиться о недействительности итератора.

Я прав, что лучше использовать map:: find(), когда есть возможность не иметь элемента с таким ключом? И map:: find() это более быстрый и элегантный подход?

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

Что касается эффективности времени, то карта обычно реализуется как дерево RB/некоторое сбалансированное двоичное дерево поиска и, следовательно, сложность O (logN) для find().

С++ Spec:

Т & operator [] (const key_type & x);
   Эффекты: Если на карте нет ключа, эквивалентного x, вставляет в карту значение_значение (x, T()).  Требуется: key_type должен быть CopyInsertable, а mapped_type -  DefaultInsertable в * это. Возвраты: ссылка на  mapped_type, соответствующий x в * this. 4 Сложность: логарифмическая.

Т & at (const key_type & x);
 const T & at (const key_type & x) const;  Возвраты: ссылка на mapped_type, соответствующая x в * this.  Выбрасывает: объект исключения типа out_of_range, если такой элемент отсутствует.  Сложность: логарифмическая.