这段代码真的是未定义的,正如 Clang 似乎表明的那样吗?

Is this code really undefined, as Clang seems to indicate?

我在使用单元测试库 Catch 的项目上启用了 -fsanitize=undefined。来自 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'

我在 clang 3.6.0 和一个使用 clang 3.4-1ubuntu3 的朋友身上发生了这种情况。我在 gcc 版本 4.9.2

上不会发生这种情况

那上面是什么?这段代码真的很糟糕,还是 clang 的结尾有什么可疑的地方?

这是来自 cfe-dev 邮件列表线程的 bug in libstdc++,标题为 -fsanitize=undefined 和共享库 说:

This is a bug in libstdc++. You will be able to work around it with a sanitizer blacklist file, once Will's patch for that lands, but for now, filtering them out manually is likely to be your best option.

Here's a patch to fix it; I'll be looking into pushing this to libstdc++ upstream in the next few days. [...]

正如我在评论中提到的那样,看到 clang 使用 libstdc++ 而不是 libc++ 的系统并不少见,如果我们在 [=22= 上测试它] 通过 -stdlib=libstdc++ 我们确实可以重现该问题。

以下 libstdc++ 错误报告:bad enum values computed by operator~ in ios_base.h 涵盖了这个问题并指出:

The overloaded operator~s defined for the enumerations in ios_base.h have the following form:

Enum operator~(Enum e) { return Enum(~static_cast<int>(e)); }

The ~ creates values outside the range of values of the enumeration type, so the cast back to the Enum type has an unspecified value (see [expr.static.cast]p10), and in practice it produces an Enum value outside the range of representable values for the Enum type, so behavior is undefined.

供参考 [expr.static.cast]p10 说:

A value of integral or enumeration type can be explicitly converted to an enumeration type. The value is unchanged if the original value is within the range of the enumeration values (7.2). Otherwise, the resulting value is unspecified (and might not be in that range). A value of floating-point type can also be converted to an enumeration type. The resulting value is the same as converting the original value to the underlying type of the enumeration (4.9), and subsequently to the enumeration type.

正如 hvd 所说,这是形式上未指定的行为,但理查德指出,实际上最终是未定义的行为。

T.C。指出这已由 DR 1766: Values outside the range of the values of an enumeration:

从未指定行为更改为未定义行为

Although issue 1094 clarified that the value of an expression of enumeration type might not be within the range of the values of the enumeration after a conversion to the enumeration type (see 5.2.9 [expr.static.cast] paragraph 10), the result is simply an unspecified value. This should probably be strengthened to produce undefined behavior, in light of the fact that undefined behavior makes an expression non-constant. See also 9.6 [class.bit] paragraph 4.

新措辞出现在N4431的标准草案中。