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

Случай использования в реальном времени для функции индексирования `at()` в библиотеке std С++?

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

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

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

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

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

Примечание. Я недавно использовал его в каком-то единичном тестовом коде, где добавление кода проверки индекса не считалось стоящим проблемой, а исключение out_of_range, созданное at(), считается достаточным info + context в случае, если тестовые перерывы.

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

Если вы не используете критическую часть приложения, вы всегда должны поддерживать std::out_of_range от Undefined Behavior, по крайней мере, мое кредо.

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

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

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

Я согласен со многими людьми, что 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.

Ответ 8

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

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

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

В других случаях используйте at