Почему допустимо поведение undefined в STL?
По умолчанию "базовый контейнер" std::stack
- это std::deque
. Поэтому все, что есть undefined поведение для std::deque
, - это поведение undefined для std::stack
. cppreference, а другие сайты используют терминологию "эффективно" при описании поведения функций-членов. Я полагаю, это означает, что это предназначено для всех целей и целей. Таким образом, вызов top()
и pop()
эквивалентен вызовам back()
и pop_back()
, а вызов их в пустом контейнере - это поведение undefined.
По моему мнению, причина, по которой это поведение undefined заключается в том, чтобы сохранить гарантию отсутствия броска. Мое рассуждение состоит в том, что operator[]
для std::vector
имеет гарантию отсутствия броска и имеет значение undefined, если размер контейнера больше N, но at()
имеет сильную гарантию и бросает std::out_of_range
, если n не входит в границы.
Итак, мой вопрос в том, что является причиной некоторых вещей, которые могут иметь поведение undefined и не имеют никакой гарантии броска по сравнению с сильной гарантией, но вместо этого выбрасывают исключение?
Ответы
Ответ 1
Когда поведение undefined разрешено, оно обычно по соображениям эффективности.
Если в стандарте указано, что должно произойти при доступе к массиву за пределами границ, это заставит реализацию проверить, находится ли индекс в границах. То же самое относится к вектору, который является просто оболочкой для динамического массива.
В других случаях поведение допускается undefined, чтобы обеспечить свободу в реализации. Но это тоже касается эффективности (поскольку некоторые возможные стратегии реализации могут быть более эффективными на некоторых машинах, чем на других, а С++ оставляет за собой возможность реализовать наиболее эффективную стратегию, если они этого желают.)
Ответ 2
Согласно Herb Sutter, одна из отмеченных причин - эффективность. Он утверждает, что стандарт не налагает никаких требований в спецификации спецификации operator[]
или не требует проверки привязки. Это зависит от реализации.
С другой стороны, vector<T>::operator[]()
разрешено, но не требуется, чтобы выполнить проверку границ. Там нет дыхания формулировки в стандартной спецификации для operator[]()
, которая говорит что-либо о проверке границ, но и нет никаких требований, чтобы это имеют спецификацию исключения, поэтому ваш стандартный исполнятель библиотеки можно также добавить проверку границ на operator[]()
. Итак, если вы используете operator[]()
запросить элемент, который не находится в векторе, вы по своему усмотрению, и стандарт не дает никаких гарантий относительно того, что будет (хотя ваша стандартная документация по внедрению библиотеки может) - ваша программа может немедленно сработать, вызов operator[]()
может генерировать исключение, или может показаться, что работа и иногда и/или таинственным образом терпит неудачу.
Учитывая, что проверка границ защищает нас от многих распространенных проблем, почему для выполнения проверки границ требуется operator[]()
? Короткий ответ: Эффективность. Всегда проверка границ приведет к (возможно, незначительные) накладные расходы на производительность для всех программ, даже тех, которые никогда не нарушайте границы. Дух C++ включает в себя изречение, которое и вам не придется платить за то, что вы не используете, и поэтому проверка границ не требуется для operator[]()
. В этом случае мы имеют дополнительную причину для достижения эффективности: векторы предназначены которые будут использоваться вместо встроенных массивов, и поэтому должны быть такими же эффективными как встроенные массивы, которые не выполняют проверку границ. Если ты хочешь быть убедитесь, что границы проверяются, вместо этого используйте at()
.
Если вам интересно узнать о преимуществах производительности, см. следующие два вопроса:
Похоже, что консенсус operator[]
более эффективен (поскольку std::vector
- это всего лишь оболочка вокруг динамического массива, operator[]
должна быть такой же эффективной, как если бы вы назовете ее на массив.) И Herb Саттер, похоже, полагает, что независимо от того, безопасно это исключение, зависит от поставщика компилятора.