Ответ 1
Стандарт С++ отсылает определение assert
к стандарту C.
" Макрос
assert
помещает диагностические тесты в программы, он расширяется до выражения void. Когда он выполняется, если выражение (которое должно иметь скалярный тип) является ложным (то есть, сравнивается с 0), макрос утверждения записывает информацию об отказе конкретного вызова (включая текст аргумента, имя исходного файла, номер строки источника и имя закрывающей функции - последние являются соответственно значения макросов предварительной обработки__FILE__
и__LINE__
и идентификатора__func__
) в стандартном файле ошибки в определенном для реализации формате. Затем он вызывает функциюabort
.
В assert(0)
0
интерпретируется как false
, поэтому это утверждение всегда терпит неудачу или огонь, когда проверка утверждения включена.
Таким образом, он утверждает, что
"Выполнение никогда не достигнет этой точки."
На практике может быть сложно заставить компиляторы заткнуться о выполнении, достигнутом или не достигшем заданной точки. Часто компилятор сначала жалуется на выполнение, возможно, доходящее до конца функции, не возвращая значения. Добавление assert(0)
должно идеально решить эту проблему, но тогда компилятор может жаловаться на assert
или не признать, что он говорит, что вы уже хорошо осведомлены о том, о чем он пытается предупредить.
Одна возможная мера (1) должна также генерировать исключение в этой точке:
auto foo( int x )
-> int
{
if( x == 1 ) { return 42; }
assert( 0 ); throw 0; // Should never get here!
}
Разумеется, что двойной удар можно определить как макрос более высокого уровня. Что касается типа исключения, вы можете сохранить его как не std::exception
, потому что это не исключение, предназначенное для того, чтобы быть пойманным обычным catch
в любом месте. Или, если вы доверяете стандартной иерархии исключений (для меня это не имеет смысла, но), вы можете использовать std::logic_error
.
Чтобы отключить проверку подтверждения assert
, вы можете определить символ NDEBUG
, прежде чем включать <assert.h>
.
Этот заголовок имеет специальную поддержку, поэтому вы можете включать его несколько раз, с или без NDEBUG
.
" Единица перевода может включать заголовки библиотек в любом порядке (раздел 2). Каждый из них может быть включен больше, чем один раз, без какого-либо эффекта, отличного от того, чтобы быть включенным ровно один раз, за исключением того, что эффект включения либо
<cassert>
, либо<assert.h>
зависит каждый раз от определения lexically currentNDEBUG
.
Разумное определение двойного удара, о котором говорилось выше, также может зависеть от NDEBUG
, не включающего охрану, например
#include <stdexcept> // std::logic_error
#include <assert.h>
#undef ASSERT_SHOULD_NEVER_GET_HERE
#ifdef NDEBUG
# define ASSERT_SHOULD_NEVER_GET_HERE() \
throw std::logic_error( "Reached a supposed unreachable point" )
#else
# define ASSERT_SHOULD_NEVER_GET_HERE() \
do{ \
assert( "Reached a supposed unreachable point" && 0 ); \
throw 0; \
} while( 0 )
#endif
Отказ от ответственности: хотя я кодировал это несколько раз в начале 2000-х годов, я приготовил код выше только для этого ответа, и, хотя я тестировал его с помощью g++, он может не быть идеальным.
(1) См. ответ Базиля Старинкевича для обсуждения другой возможности, g++ - специфического внутреннего __builtin_unreachable
.