Я включил -fsanitize=undefined
на моем проекте, который использует Catch, библиотеку модульного тестирования. Одна строка из Catch была сигнализирована как вызывающая неопределенное поведение этого флага. Мне удалось сделать отдельный пример:
#include <iomanip>
#include <sstream>
int main()
{
std::ostringstream os;
os << "0x" << std::setfill('0') << std::hex;
}
Составлено с:
clang++ -fsanitize=undefined main.cpp
Если я запускаю это, выдается следующий отпечаток:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:96:24: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.9.2/../../../../include/c++/4.9.2/bits/ios_base.h:76:67: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'
Это происходит для меня на лязг 3.6.0
и для друга с лязгом 3.4-1ubuntu3
, Это не происходит для меня на версии GCC 4.9.2
Так что здесь? Этот код на самом деле плохой, или что-то подозрительное происходит на конце Clang?
Это ошибка в libstdc ++, от cfe-dev
список рассылки с заголовком -fsanitize = неопределенные и общие библиотеки говорит:
Это ошибка в libstdc ++. Вы сможете обойти это с
файл черного списка дезинфицирующего средства, однажды патч Уилла для этой земли, но для
Теперь их фильтрация, вероятно, будет лучшим вариантом.Вот патч, чтобы это исправить; Я буду смотреть на это
libstdc ++ upstream в ближайшие несколько дней. […]
Как я отмечал в комментариях, это не редкость видеть системы, где clang
использования libstdc++
в отличие от libc++
и если мы проверим это на Coliru явно использует libstdc ++ с помощью -stdlib=libstdc++
мы действительно можем воспроизвести проблему.
Следующие libstdc++
сообщение об ошибке: неверные значения перечисления, вычисленные оператором ~ в ios_base.h освещает эту проблему и говорит:
Перегруженный оператор ~ определен для перечислений в ios_base.h
иметь следующую форму:Enum operator~(Enum e) { return Enum(~static_cast<int>(e)); }
~ Создает значения вне диапазона значений перечисления
тип, поэтому приведение к типу Enum имеет неопределенное значение (см.
[expr.static.cast] p10), и на практике он выдает значение Enum
вне диапазона представимых значений для типа Enum, так
поведение не определено.
Для справки [Expr.static.cast] р10 говорит:
Значение целочисленного типа или типа перечисления может быть явно преобразовано в тип перечисления. Значение
не изменяется, если исходное значение находится в диапазоне значений перечисления (7.2). В противном случае, в результате
значение не указано (и может не входить в этот диапазон). Значение типа с плавающей точкой также может быть преобразовано
к типу перечисления. Полученное значение совпадает с преобразованием исходного значения в базовый
тип перечисления (4.9), а затем и тип перечисления.
и, как говорит Х.В.Д., это формально неуказанное поведение, но Ричард отмечает, что на практике это в конечном итоге неопределенное поведение.
Агар Т.С. указывает, что это было изменено с неопределенного на неопределенное поведение DR 1766: значения вне диапазона значений перечисления:
Хотя в выпуске 1094 поясняется, что значение выражения типа перечисления может не входить в диапазон значений перечисления после преобразования в тип перечисления (см. Пункт 5.2.9 [expr.static.cast], пункт 10), результат это просто неопределенное значение. Это, вероятно, следует усилить, чтобы получить неопределенное поведение, в свете того факта, что неопределенное поведение делает выражение непостоянным. См. Также 9.6 [class.bit] параграф 4.
Новая формулировка появляется в проекте стандарта в N4431.