Реальный сценарий использования для функции индексации at () в библиотеке C ++ std? - PullRequest
22 голосов
/ 13 апреля 2011
Контейнер

C ++ vector, deque, ... обеспечивает функцию доступа at(index) в дополнение к operator[index] для доступа к элементам контейнера.

Разница между этимфункция-член и оператор-член оператор-функция [] состоит в том, что deque :: at сигнализирует, если запрошенная позиция выходит за пределы диапазона, вызывая исключение out_of_range.

Мне никогда не требовалась эта функцияв моем коде, поскольку это никогда не имеет смысла в моем коде C ++ для доступа к элементам, которые, возможно, выходят за пределы допустимого диапазона.Код всегда пишется для доступа к правильным индексам (или выдает значимую ошибку / исключение в случае, если индексы не могут быть сопоставлены.)

Я был бы заинтересован в реальных примерах (возможно, из какого-то проекта с открытым исходным кодом, поскольку это добавило бы некоторый контекст), где at() используется в производственном коде.

Может быть, кто-то может привести пример алгоритмической проблемы, в которой использование at() имело смысл.

Примечание: я недавно использовал в некотором коде модульного теста, где добавление кода проверки индекса не стоило проблем, а исключение out_of_range, выданное at(), считаетсядостаточно информации + контекст на случай, если тест пройдёт.

Примечание: Относительно этого ответа от ildjarn - я не хочу начать обсуждение или комментировать войнуна этом.Я заинтересован в «положительных» находках, то есть конкретных примерах , где он использовался .Спасибо.

Ответы [ 8 ]

13 голосов
/ 13 апреля 2011

Что ж, когда вы не контролируете используемый индекс (например, передается ли он клиентом вашего кода), вы должны либо проверить его, чтобы убедиться, что он находится в диапазоне вручную, либо использовать at, чтобы получить сообщение об исключении (которое вы можете зафиксировать и уведомить вызывающего абонента с помощью собственного сообщения об ошибке или просто распространить стандартное исключение вверх).

Другими словами, вызываемая функция несет ответственность за проверку входных параметров, но вопрос о том, делает ли она это явно с помощью оператора if или неявно, используя at вместо [], является предметом обсуждения. Если все, что я собираюсь сделать, это все равно выдать исключение out_of_range (если переданный индекс больше или равен размеру коллекции), я думаю, что просто позволю at сделать это и сохранить себя немного кодирования.

Передача плохих данных без вывода сообщений почти никогда не является лучшим решением. Проблема с простым возвратом x [7] для целочисленной колоды из четырех элементов заключается в том, что вызывающая сторона думает, что это действительный ноль. Это не дело.

7 голосов
/ 13 апреля 2011

На мой взгляд, at() - это 100% бесполезная функция-член. Доступ только в пределах допустимых границ стандартного библиотечного контейнера является предварительным условием использования этого контейнера, и нарушения любого предварительного условия должны обрабатываться с assert вместо того, чтобы вызывать исключение , Существование at() никоим образом не помогает контейнеру поддерживать свои предварительные условия / инварианты, и фактически только запутывает проблему, делая правильный проверенный доступ доступом, по-видимому, не быть предварительным условием.

Т.е., исключение для чего-то, что в конечном итоге может быть вызвано только ошибкой программиста - это глупо. См. Эту ветку для более подробного объяснения, в частности, постов Д. Абрахамса; Как бы долго это ни было, но стоит прочитать: comp.lang.c ++. moderated: исключения .

РЕДАКТИРОВАТЬ: Чтобы уточнить в ответ на добавленную заметку OP, я говорю, что в моем опыте с C ++ - профессионально, с открытым исходным кодом, и в противном случае - я никогда не сталкивался с использовать стандартные контейнеры at() и утверждать, что на самом деле не используется в производственном коде. Дальнейшие комментарии или уточнения должны были просто объяснить, почему я думаю, что это так.

6 голосов
/ 15 апреля 2011

Один случай использования, в котором я постоянно находил at(), полезен для облегчения анализа сложного пользовательского ввода.Например, анализируя код C ++, я обнаруживаю, что двигаюсь по массиву лексических токенов, когда проверяю грамматические конструкции.Логика часто звучит так: «Если этот токен является Идентификатором, а следующий - Равенством, то это должно быть присвоение, поэтому просмотрите маркер точки с запятой, чтобы определить диапазон токенов для выражения».Использование at() в таком коде означает, что вы можете легко выразить ожидания с некоторым смещением относительно текущей точки, аля:

if (tokens.at(i) == Switch)
{
    if (tokens.at(++i) != Left_Parentheses)
        // throw or return to say input program's broken...
    if (tokens.at(++i) == ...)
        ...
}

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

FWIW - быстрый поиск нашего производственного кода (200 тыс. Строк, большинство написано до того, как я присоединился к команде) нашелдюжина использований at().

5 голосов
/ 13 апреля 2011

Мой случай скорее всего будет: почему бы не использовать его ?

Если вы не находитесь в критически важной части вашего приложения, вы всегда должны отдавать предпочтение std::out_of_range против Не определеноПоведение , по крайней мере, это мое кредо.

На практике я обычно преобразую весь код, над которым работаю, для использования проверенных обращений.Нарушение производительности невидимо для большей части кода, и по крайней мере у меня есть хороший отчет с информацией о текущем контексте выполнения (сгенерированный в catch(std::exception const&) на корневом уровне), а не повреждение памяти, которое делает мой коднесколько раз спустя (или, что еще хуже, похоже, это сработало).

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

Использование [] вместо at() похоже на ношение заряженного оружия без / с (соответственно) защитой в кармане.Вы можете забыть надеть его, но охотно снимаете?Это безумие.

4 голосов
/ 13 апреля 2011

После быстрого поиска я обнаружил, что, среди прочего, Inkscape (редактор svg), Google v8, Android, Chromium и Ogre использовали эту функцию.Этот (элементарный) список был взят из простого поиска Google с использованием регулярного выражения at\([0-9]+\).

Использование \.at\([0-9a-z_]+\) вместо предыдущего выражения дает более общие результаты и добавляет OpenJdk и множество проектов sourceforge.

0 голосов
/ 22 ноября 2012

Страуструп рекомендует использовать at во всех местах, кроме случаев, когда вы уверены, что индекс будет в допустимом диапазоне.

Он рекомендует использовать для кода, подобного приведенному ниже, оператор [].

for (int i = 0; i < v.size(); ++i)
{
    // use v[i] - we are sure it will be a valid index
}

В остальных случаях используйте at

0 голосов
/ 13 апреля 2011

Я согласен со многими здесь, что at в основном бесполезен;однако это может выглядеть лучше при работе с указателями (или списками) контейнеров:

std::vector<std::vector<int> >::iterator i;
for (i = v2.begin(); i != v2.end(); ++i)
{
    int x1 = (*i)[3]; // UGLY
    int x2 = i->at(3); // Looks OK
}

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

0 голосов
/ 13 апреля 2011

v.at(-1) не выйдет из строя, как v[-1] (вы получите исключение)

...