Является ли nullptr ложью?
Является ли nullptr
постоянно ложным при использовании в качестве логического выражения или в явном или неявном виде в логическом выражении? Эта реализация определена или определена в стандарте?
Я написал некоторый код для тестирования, но не уверен, что он полностью проверяет это свойство. Я не мог найти существующий ответ SO, который говорил конкретно об этом. cppreference не упоминает об этом из того, что я вижу.
if (nullptr) {
;
} else {
std::cout << "Evaluates to false implicitly\n";
}
if (!nullptr) {
std::cout << "Evaluates to false if operated on\n";
}
if (!(bool)(nullptr)) {
std::cout << "Evaluates to false if explicitly cast to bool\n";
}
Ожидаемый и фактический:
Evaluates to false implicitly
Evaluates to false if operated on
Evaluates to false if explicitly cast to bool
Ответы
Ответ 1
В соответствии со стандартом C++ 17 (5.13.7 литералов указателей)
1 Литерал указателя - это ключевое слово nullptr. Это тип значения станд :: nullptr_t. [Примечание: std::nullptr_t - это отдельный тип, который ни тип указателя, ни тип указателя на член; скорее, prvalue этот тип является константой нулевого указателя и может быть преобразован в значение нулевого указателя или значение указателя нулевого элемента. Смотри 7.11 и 7.12. - конечная нота]
И (7 стандартных преобразований)
4 Некоторые языковые конструкции требуют преобразования выражения к логическому значению. Выражение e, встречающееся в таком контексте, называется контекстно преобразованным в bool и хорошо сформирован, если и только если декларация bool t (e); хорошо сформирован, для некоторых изобретен временная переменная t (11.6).
И наконец (7.14 Булевых преобразований)
1 Значение арифметики, перечисление с незаданной областью, указатель или Тип указатель на член может быть преобразован в тип значения bool. нулевое значение, нулевое значение указателя или нулевое значение указателя элемента преобразуется в ложь; любое другое значение преобразуется в true. Для прямой инициализацией (11.6), значение типа std::nullptr_t может быть преобразован в значение типа bool; Полученное значение равно false.
То есть ты можешь написать например
bool b( nullptr );
но вы не можете писать (хотя некоторые компиляторы имеют ошибку по этому поводу)
bool b = nullptr;
Таким образом, nullptr
может быть контекстно преобразован в объект типа bool, например, в операторах выбора, таких как оператор if.
Рассмотрим, например, унарный оператор !
как в операторе if
if ( !nullptr ) { /*...*/ }
Согласно описанию оператора (8.5.2.1 Унарные операторы)
9 Операнд оператора логического отрицания! контекстуально преобразован в bool (пункт 7); его значение истинно, если преобразованный операнд ложный и ложный в противном случае. Тип результата: bool
Поэтому nullptr
в этом выражении не преобразуется в указатель. Это напрямую по контексту преобразуется в bool.
Ответ 2
Результат вашего кода гарантирован, [dcl.init]/17.8
В противном случае, если инициализация является прямой инициализацией, тип источника - std::nullptr_t
, а тип назначения - bool
, начальное значение инициализируемого объекта - false
.
Это означает, что для прямой инициализации объект bool
может быть инициализирован из nullptr
со значением результата false
. Затем для (bool)(nullptr)
nullptr
преобразуется в bool
со значением false
.
При использовании nullptr
в качестве условия if
или операнда operator!
он рассматривается как контекстные преобразования,
неявное преобразование выполняется, если объявление bool t(e);
правильно сформировано
Это означает, что if (nullptr)
и !nullptr
, nullptr
будут преобразованы в bool
со значением false
.
Ответ 3
Да, но вы должны избегать использования этого факта.
Сравнение указателей на false
или 0
является распространенным типом кодирования C/C++. Я предлагаю вам избегать его использования. Если вы хотите проверить на ничтожность, используйте:
if (x == nullptr) { /* ... */}
а не
if (!x) { /* ... */}
или
if (not x) { /* ... */}
Второй вариант добавляет читателю еще одну путаницу: что такое x
? Это логическое значение? Простое значение (например, целое число)? Указатель? Необязательно? Даже если x
имеет осмысленное имя, оно вам мало чем поможет: if (!network_connection)
... это все же может быть сложная структура, конвертируемая в целое или логическое значение, это может быть логический индикатор того, существует ли соединение, это может быть указатель, значение или необязательно. Или что-то еще.
Кроме того, помня, что nullptr
оценивает как ложное, это еще один бит информации, который вам нужно хранить в глубине своего мозга, чтобы правильно декодировать код, который вы читаете. Мы можем привыкнуть к этому с давних времен или читать код других людей - но если бы мы этого не делали, не было бы очевидно, что nullptr
ведет себя так. В некотором смысле это не отличается от других неясных гарантий, например, как значение в индексе 0 пустого std::string
гарантированно будет \0
. Только не заставляйте свой код полагаться на это, если только вам это не нужно.
PS: на самом деле в настоящее время использование нулевых указателей намного меньше. Вы можете заставить указатель никогда не быть нулевым, если им это не нужно; вы можете использовать ссылки вместо указателей; и вы можете использовать std::optional<T>
, чтобы вернуть либо T
, либо "no T". Возможно, вы могли бы просто не упомянуть nullptr
в целом.