В Visual Studio макрос assert определяется следующим образом:
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
Я понимаю, что правильная часть после || оценивается только в том случае, если левое выражение ложно, и выдает исключение.
У меня есть 3 вопроса:
!! двойное отрицание — это преобразование типа «выражение» в логическое значение, верно? Что было бы, если бы они остались в стороне? Выражение 'будет неявно преобразовано в логическое значение, когда оно будет логически объединено с другим выражением, верно? В:
поплавок() || /* Вызов функции, которая выбрасывает*/;
В любом случае, float() преобразуется в тип bool?
2.После || оператор у нас есть:
( /* call function that throws */ , 0 )
Запятая — это точка последовательности, поэтому сначала вызывается функция, а затем это выражение оценивается как 0. Если бы вызов функции не завершал программу, тогда выражение было бы просто:
!!(expression)) || 0
Какой смысл в этом 0 после запятой? Если бы их не было, то выражение было бы таким:
!!(expression)) || / * call function that throws */
Что то же самое, верно?
Оператор запятая необходим, потому что _wassert
возвращает void
. А _wassert не throw
, он либо прерывается с кодом выхода 3, врывается в отладчик, либо игнорирует проблему и продолжает работу. Его поведение можно изменить с помощью _set_error_mode
. Макрос также избегает проблемы висячего else, используя макрос с оператором if
, и он немного умен, потому что он сохраняет его как выражение, а не оператор.
Самый верхний уровень (void)
предназначен для подавления предупреждения компилятора о неиспользованном результате выражения, а также для защиты от случайного использования результата выражения. И макрос, вероятно, тот же, что используется при компиляции C, C++ или C++/CLI, поэтому он должен соответствовать пересечению этих языков.
@Элджей, круто, теперь сделай ответ :)
Разберем код:
#define assert(expression) (void)( \
(!!(expression)) || \
(_wassert(_CRT_WIDE(#expression), _CRT_WIDE(__FILE__), (unsigned)(__LINE__)), 0) \
)
(void)
должен подавить предупреждение компилятора о том, что результат выражения не используется. А также гарантирует, что утверждение не будет случайно использовано как часть более крупного выражения.
!!
часть !!(expression)
должна гарантировать, что выражение оценивается как false
или true
.
Но озорной код C++ мог переопределить operator!
и вызвать здесь небольшой хаос.
Почему актерский состав в стиле C, а не static_cast<bool>
? Я подозреваю, что один и тот же заголовочный файл используется для поддержки C и C++, а также, возможно, C++/CLI. (Если это так, может быть оправдано использование static_cast<bool>
для C++ и (bool)
для C в макросах для двух языков.)
Если объект expression
имеет преобразование operator bool
, он будет использоваться !!
. (Но также был бы использован (bool)(expression)
.)
Несмотря на это, наблюдение о том, что !!
не нужно, кажется мне правильным. Выражения в C++, которые являются истинными (ненулевыми) или ложными (нулевыми), являются неотъемлемой частью языка.
Оператор запятой (x(), 0)
используется, потому что x
(в данном случае _wassert
) возвращает пустоту. Невозможно сделать a || b
, если b
недействительно, поэтому хитрость просто делает так, что пустота вызывается, а выражение приводит к int
. Неважно, что это 0
.
_wassert не бросает. Он может прерваться с кодом выхода 3. Он может взломать отладчик.
Его можно проигнорировать и продолжить. И с нарушением утверждения могут быть последствия сломанной оси, или код может иметь какое-то защитное программирование для обработки нарушения утверждения.
Поскольку макрос, подобный функции assert, был написан как выражение, он аккуратно избегает проблемы «висячего макроса if
» (если бы использовалась конструкция if (!(expression)) ...
) и является выражением, а не оператором.
Что, если у меня есть тип, который (по глупости? Давайте возьмем на себя сомнения и предположим, что на то есть веские причины) переопределен
operator||
? Хм, наверное,((bool)(expression) || ...
было бы безопаснее. Они также могли переопределитьoperator!
.