Должно ли логическое значение быть усеченным до true или false при назначении?

Я обнаружил разницу в значении, хранящемся в переменной bool (между прочим, Visual-C ++ и clang ++), в случае, когда хранимое значение не является ни истинным, ни ложным (если оно было каким-либо образом повреждено), и я не уверен, что это ошибка Visual C ++ или, если это просто UB, я должен игнорировать.

Возьмите следующий образец:

#include <cstdint>
#include <iostream>
#include <string>
#include <limits>
bool inLimits(bool const v)
{
return (static_cast<std::int32_t>(v) >= static_cast<std::int32_t>(std::numeric_limits<bool>::min()) && static_cast<std::int32_t>(v) <= static_cast<std::int32_t>(std::numeric_limits<bool>::max()));
}
int main()
{
bool b{ false };
bool const* const pb = reinterpret_cast<bool const*>(&b);
std::uint8_t * const pi = reinterpret_cast<std::uint8_t*>(&b);

std::cout << "b: " << b << " pb: " << (*pb) << " pi: " << std::to_string(*pi) << std::endl;
std::cout << "b is " << (inLimits(b) ? "" : "not ") << "in numeric limits for a bool" << std::endl;

*pi = 3; // Simulate a bad cast during boolean creation
bool const b2{ b };
bool const b3{ *pb };

std::cout << "b: " << b << " pb: " << (*pb) << " pi: " << std::to_string(*pi) << std::endl;
std::cout << "b2: " << b2 << " b3: " << b3 << std::endl;

std::cout << "b is " << (inLimits(b) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
std::cout << "b2 is " << (inLimits(b2) ? "" : "not ") << "in numeric limits for a bool" << std::endl;
std::cout << "b3 is " << (inLimits(b3) ? "" : "not ") << "in numeric limits for a bool" << std::endl;

return 0;
}

Это вывод Visual-C ++

b: 0 pb: 0 pi: 0
b is in numeric limits for a bool
b: 3 pb: 3 pi: 3
b2: 3 b3: 3
b is not in numeric limits for a bool
b2 is not in numeric limits for a bool
b3 is not in numeric limits for a bool

и это вывод Clang ++

b: 0 pb: 0 pi: 0
b is in numeric limits for a bool
b: 1 pb: 1 pi: 3
b2: 1 b3: 1
b is in numeric limits for a bool
b2 is in numeric limits for a bool
b3 is in numeric limits for a bool

Похоже, что в clang ++ есть проверка пределов при создании нового логического значения, а также при его использовании с оператором потока.

Должен ли я просто игнорировать это или это ошибка, которая есть только в Visual-C ++?
Спасибо!

Редактировать:
Для тех, кто не понимал цели примера, это была просто демонстрация, чтобы «симулировать» повреждение памяти или ошибку в другой части кода, которая вызвала инициализацию логического значения чем-то, кроме true или false, независимо от того, двоичное представление bool.

(Мне было интересно, должен ли я защищать свой код от неправильного использования где-либо еще, например, с помощью assert, но только если это поведение не UB)

Второе редактирование:
Добавлен код numeric_limits.

3

Решение

Стандарт не уточняет, какое значение представляет bool является . Компиляторы могут создавать свои собственные спецификации.

Ваше свидетельство предполагает, что VC ++ требует true быть представленным как просто набор LSB, тогда как clang ++ позволяет любому ненулевому представлению быть true,

Для VC ++ ваш код вызывает неопределенное поведение в строке bool const b2{ b };в частности, когда он пытается прочитать значение из b, Биты, установленные в хранилище для b не соответствуют значению bи стандарт не определяет, что происходит в этой ситуации, поэтому это неопределенное поведение.

Когда происходит неопределенное поведение, нет никаких гарантий; весь вывод программы бессмыслен. Вы не можете вывести что-либо на основе выходных операторов, которые появляются после этой точки (или даже до нее, на самом деле).

2

Другие решения

«в случае, когда сохраненное значение не является ни истинным, ни ложным»

Почему вы думаете, что это так? C ++ не ограничивает двоичное представление bool, На некоторых компиляторах true может быть представлен 00000011и другие компиляторы могли бы представлять false как 00000011,

Но на самом деле ни GCC, ни MSVC не используют этот битовый шаблон для bool значение. Это делает его действительно неопределенным поведением. UB может никогда быть ошибкой компилятора. Ошибка заключается в том, что реализация не работает должным образом, но UB специально означает, что любое реальное поведение приемлемо.

3

Так как я действительно не нашел информацию о приведениях от указателя к bool (или эквивалентного) в стандарте C ++ (если определено их использование), я не хотел публиковать это как ответ. Но, со второй мысли, я могу также опубликовать это — это может быть разработано другими людьми.

Прежде всего, стандарт C ++ 14 определяет bool как:

[Basic.fundamental]
  1. Значения типа bool являются либо истиной, либо ложью. [Примечание: не существует типов или значений со знаком, без знака, с коротким или длинным типом bool. — примечание конца] Значения типа bool участвуют в интегральных повышениях (4.5)

Поскольку он участвует в интеграционных акциях, для него определена следующая акция:

[Conv.prom]
  1. Prvalue типа bool может быть преобразовано в prvalue типа int, где false становится равным нулю, а true становится единым.

И, так как вы печатаете с std::ostream::operator<<, за boolопределяется следующим образом:

[Ostream.inserters.arithmetic]
  1. Классы num_get<> и num_put<> обрабатывать зависящее от локали числовое форматирование и анализ.

Так как он использует num_put<> для фактического вывода, его фрагмент, связанный с bool Выход определяется как:

[facet.num.put.virtuals]
  1. Если (str.flags () & ios_base :: boolalpha) == 0 возвращает do_put (out, str, fill, (int) val)

Так как вы не используете boolalpha в приведенном вами примере — должны применяться типичные интегральные правила продвижения (описанные выше).

Кроме того, я до сих пор не могу объяснить, почему std::to_string(*pi) после *pi = 3 все еще печатает 3 в обоих случаях, но это может как-то быть связано с:

[Expr.reinterpret.cast]
  1. [Примечание: отображение, выполненное reinterpret_cast, может или не может создать представление, отличное от исходного значения. — примечание конца]
1

Не уверен, поможет ли это, но g ++ демонстрирует то же поведение, что и Visual-C ++.

Это вывод, который я получил:

b: 0 pb: 0 pi: 0
б: 3 пб: 3 пи: 3
b2: 3 b3: 3

Из того, что я понимаю (я эксперт по компиляторам c ++), reinterpret_cast инструктирует компилятор обрабатывать коллекцию битов как новый тип. Поэтому, когда вы говорите компилятору переосмыслить адрес логическое значение как 8-разрядное целое число, по сути, также преобразует исходное логическое значение в 8-разрядное целое число (если это имеет смысл).

Так что, если моя интерпретация правильный (это не так), возможно, это «ошибка» в clang ++, а не в Visual или g ++. reinterpret_cast не очень хорошо поддерживается между компиляторами, так что это поведение определенно стоит отметить при принятии решения, какой использовать, если это необходимо по какой-либо причине.

редактировать:

Я только что понял, что это не объясняет, почему b2 и b3 равны 3 (не булево). Я бы не подумал, что имеет смысл рассматривать новые логические значения как 8-битные целые числа, независимо от reinterpret_castтак что возьми это за то что стоит у парня с 1 репом 🙂

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector