Как указывали другие, assert
это своего рода последний оплот защиты от ошибок программистов, которые никогда не должны происходить. Это проверки работоспособности, которые, мы надеемся, не потерпят неудачу влево и вправо к моменту отправки.
Он также предназначен для исключения из стабильных сборок релиза по любым причинам, которые могут оказаться полезными для разработчиков: эстетика, производительность, все, что они захотят. Это часть того, что отделяет отладочную сборку от сборки выпуска, и по определению сборка выпуска лишена таких утверждений. Таким образом, существует подрыв дизайна, если вы хотите выпустить аналогичную «сборку релиза с утверждениями на месте», которая была бы попыткой сборки релиза с определением _DEBUG
препроцессора и без NDEBUG
определения; это больше не релизная версия.
Дизайн распространяется даже на стандартную библиотеку. В качестве очень простого примера среди множества других реализаций std::vector::operator[]
будет assert
проверка работоспособности, чтобы убедиться, что вы не проверяете вектор вне границ. И стандартная библиотека начнет работать намного, намного хуже, если вы включите такие проверки в сборке релиза. Ориентирvector
использованияoperator[]
и заполнитель ctor с такими утверждениями, включенными в простой старый динамический массив, будет часто показывать, что динамический массив работает значительно быстрее, пока вы не отключите такие проверки, поэтому они часто влияют на производительность далеко, далеко не тривиальными способами. Проверка нулевого указателя здесь и проверка вне границ могут действительно стать огромными расходами, если такие проверки применяются миллионы раз для каждого кадра в критических циклах, предшествующих коду, так же просто, как разыменование умного указателя или доступ к массиву.
Таким образом, вы, скорее всего, нуждаетесь в другом инструменте для работы, который не предназначен для исключения из сборок релизов, если вы хотите сборки релизов, которые выполняют такие проверки работоспособности в ключевых областях. Самым полезным, что я лично нахожу, является логирование. В этом случае, когда пользователь сообщает об ошибке, все становится намного проще, если он прикрепляет журнал, а последняя строка журнала дает мне большое представление о том, где произошла ошибка и что это может быть. Затем, после воспроизведения их шагов в отладочной сборке, я мог бы также получить ошибку подтверждения, и эта ошибка подтверждения дополнительно дает мне огромные ключи для оптимизации моего времени. Тем не менее, поскольку ведение журнала является относительно дорогим, я не использую его для применения проверок работоспособности на очень низком уровне, таких как проверка доступа к массиву вне границ в общей структуре данных.
И все же, наконец, и в некоторой степени с вами согласился, я мог видеть разумный случай, когда вы могли бы захотеть передать тестерам что-то похожее на отладочную сборку во время альфа-тестирования, например, с небольшой группой альфа-тестеров, которые, скажем, подписали NDA , Там это может упростить альфа-тестирование, если вы дадите своим тестировщикам нечто иное, чем полная сборка релиза, с некоторой отладочной информацией, прикрепленной к некоторым функциям отладки / разработки, таким как тесты, которые они могут выполнить, и более подробный вывод во время работы программного обеспечения. Я, по крайней мере, видел, как некоторые крупные игровые компании делают такие вещи для альфы. Но это для чего-то вроде альфа-тестирования или внутреннего тестирования, когда вы искренне пытаетесь дать тестерам нечто иное, чем сборка релиза. Если вы на самом деле пытаетесь отправить релизную сборку, то по определению_DEBUG
определяется, иначе это действительно сбивает с толку разницу между сборкой «отладка» и «выпуск».
Почему этот код должен быть удален перед выпуском? Проверки не сильно снижают производительность, и если они терпят неудачу, есть определенно проблема, о которой я бы предпочел более прямое сообщение об ошибке.
Как указано выше, проверки не обязательно являются тривиальными с точки зрения производительности. Многие из них, вероятно, тривиальны, но опять же, даже стандартная библиотека использует их, и это может повлиять на производительность неприемлемым образом для многих людей во многих случаях, если, скажем, обход произвольного доступа std::vector
занял в 4 раза больше времени в том, что должно быть оптимизированной сборкой выпуска из-за его проверки границ, которая никогда не должна потерпеть неудачу.
В предыдущей команде нам фактически пришлось сделать так, чтобы наша библиотека матриц и векторов исключала некоторые утверждения в определенных критических путях, просто чтобы ускорить отладочные сборки, потому что эти утверждения замедляли математические операции более чем на порядок до точки, где это было Начинаем требовать от нас подождать 15 минут, прежде чем мы сможем даже найти интересующий нас код. Мои коллеги на самом деле хотели просто удалитьasserts
прямо, потому что они обнаружили, что просто делать это имело колоссальную разницу. Вместо этого мы остановились на том, чтобы заставить критические пути отладки избегать их. Когда мы заставили эти критические пути напрямую использовать векторные / матричные данные, не проходя проверку границ, время, необходимое для выполнения полной операции (которая включала в себя не только векторную / матричную математику), сократилось с минут до секунд. Так что это крайний случай, но определенно утверждения не всегда незначительны с точки зрения производительности, даже близко.
Но также это просто способ asserts
, которым разработаны. Если они не оказали такого огромного влияния на производительность по всем направлениям, то я мог бы предпочесть его, если бы они были спроектированы как нечто большее, чем функция отладочной сборки, или мы могли бы использовать, vector::at
который включает проверку границ даже в сборках релизов и выбросы за пределы доступ, например (пока с огромным ударом по производительности). Но в настоящее время я нахожу их дизайн намного более полезным, учитывая их огромное влияние на производительность в моих случаях, как функцию только для отладочной сборки, которая не указывается при NDEBUG
определении. По крайней мере, для случаев, с которыми я работал, сборка релиза имеет огромное значение для исключения проверок работоспособности, которые никогда не должны были вообще сбоить.
vector::at
против vector::operator[]
Я думаю, что различие этих двух методов лежит в основе этого, а также альтернативы: исключения. vector::operator[]
Реализации, как правило, assert
гарантируют, что доступ за пределами границ вызовет легко воспроизводимую ошибку при попытке доступа к вектору за пределами границ. Но разработчики библиотеки делают это с предположением, что в оптимизированной сборке релиза это не будет стоить ни копейки.
В то же время vector::at
предоставляется, который всегда проверяет и выходит за границы даже в сборках релиза, но это снижает производительность до такой степени, что я часто вижу гораздо больше кода, vector::operator[]
чем использую vector::at
. Большая часть дизайна C ++ перекликается с идеей «плати за то, что ты используешь / нуждаешься», и многие люди часто одобряют operator[]
это, даже не заботясь о проверке границ в сборках релизов, основываясь на том, что они надевают не нужно проверять границы в их оптимизированных сборках. Внезапно, если утверждения были включены в сборках выпуска, производительность этих двух была бы идентична, и использование вектора всегда заканчивалось бы медленнее, чем динамический массив. Таким образом, огромная часть дизайна и преимуществ утверждений основана на идее, что они становятся свободными в сборке релиза.
release_assert
Это интересно после обнаружения этих намерений. Естественно, у всех будут разные варианты использования, но я думаю, что я нашел бы какое-то применение для, release_assert
который выполняет проверку и вылетает программное обеспечение, показывая номер строки и сообщение об ошибке, даже в сборках выпуска.
Это было бы для некоторых неясных случаев в моем случае, когда я не хочу, чтобы программное обеспечение корректно восстанавливалось, как если бы было выброшено исключение. Я бы хотел, чтобы в таких случаях он приводил к сбою даже при выпуске, чтобы пользователю можно было дать номер строки, чтобы сообщать, когда программное обеспечение обнаруживает что-то, что никогда не должно происходить, все еще в сфере проверок работоспособности на наличие ошибок программиста, а не внешних ошибок ввода, таких как исключения, но достаточно дешевые, чтобы сделать это, не беспокоясь о его стоимости в выпуске.
На самом деле есть некоторые случаи, когда я обнаружил бы серьезный сбой с номером строки и сообщением об ошибке, предпочтительнее изящного восстановления после выброшенного исключения, которое может быть достаточно дешевым, чтобы его можно было сохранить в выпуске. И в некоторых случаях невозможно выполнить восстановление из исключения, например ошибка, возникшая при попытке восстановления из существующего. Там я бы нашел идеальную подгонку для release_assert(!"This should never, ever happen! The software failed to fail!");
и, естественно, это было бы очень дешево, так как проверка была бы выполнена внутри исключительного пути в первую очередь и не стоила бы ничего в обычных путях выполнения.