使用较新的编译器缩小转换错误

Narrowing conversion error using newer compiler

我正在尝试在一个新系统上编译我的代码,但我突然 运行 遇到了我的一个旧库的麻烦。这是导致问题的代码示例片段:

int main() {
    static const unsigned char pad_block[8] = {
    '\x80', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};
}

我正在使用 g++ -o test main.cpp 编译它。我的旧系统使用 g++ 4.8.5,它可以毫无问题地编译——甚至没有警告。在带有 g++ 7.5.0 的较新系统上,出现以下错误:

main.cpp: In function ‘int main()’:
main.cpp:3:67: error: narrowing conversion of ‘'777777600'’ from ‘char’ to ‘unsigned char’ inside { } [-Wnarrowing]
     '\x80', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00'};

我了解缩小转换的一般问题,但我不了解这个具体案例。我的问题:

Why does this issue suddenly appear, without specifying a new C++ version?

较新的 releases/versions 'same' 编译器通常比 earlier/older 有更严格的要求(在符合 C++ 标准方面)。你的情况似乎是这样。

Where does the number '777777600' come from?

这是 '\x80' 文字 (-128) 的值,表示为八进制的 32 位整数。由于以下规则(引用自 this Draft C11 Standard),该值被符号扩展为 int(在您的系统上为 32 位):

5.13.3 Character literals


2   … A multicharacter literal, or an ordinary character literal containing a single c-char not representable in the execution character set, is conditionally-supported, has type int, and has an implementation-defined value.

-128 似乎不是您平台执行集中的 'representable character'。但是,为什么 g++ 编译器选择使用八进制不是我可以以任何权威方式回答的问题。 (也许是传统?)

I'd prefer not to change the library - is there another way around this?

可能是一个降低编译器严格性的命令行编译器开关; MSVC 有 /permissive 标志来关闭“一致性模式”,但我不确定 g++ 等价物是什么(或者即使有)。如果做不到这一点,对您的代码进行相当微不足道的更改,以删除对 unsigned char 数据(无论如何,表示为原始十六进制值)的字符文字的使用将起作用:

int main()
{
    static const unsigned char pad_block[8] = { 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
    //...
}

Why does this issue suddenly appear, without specifying a new C++ version?

您的问题本质上与以下内容相同:

void foo(char x) {
    unsigned char y = {x};
}

此语法在 C++11 中引入,对隐式转换应用更严格的规则。但是,特定的 C++11 功能通过对数组应用这些更严格的规则来追溯更改数组的大括号初始化的工作方式。这主要是因为它们看起来很相似,否则会造成混淆。

由于引入了重大更改,从 GCC 5.0 开始,g++ 已开始将这些 C++11 故障报告为 C++03 代码中的警告,以简化过渡。您可以在以下答案中找到更多详细信息:

你的另外两个问题在另一个答案中已经很好地涵盖了,但可以添加一点:

Where does the number '777777600' come from?

除了@AdrianMole 所说的之外,值得一提的是,从 '[=12=]x80''777777600' 的这种转换似乎完全是 gcc 报告过程的内部过程,以下断言证明了这一点通过。它需要 -std=c++11,但您的代码会产生完全相同的错误,因此可以说它等效地适用。

static_assert(std::is_same<char, decltype('\x80')>::value, "");

I'd prefer not to change the library - is there another way around this?

如果库是单独编译的,将 -Wno-narrowing 添加到其编译器选项将使错误消失而无需触及代码,但这确实 运行 隐藏了更严重的违规行为的风险 if/when 你更新库代码。将 char 文字更改为 int 文字是一个更好的全面修复,除非你需要做很多这样的事情并且维护补丁会很痛苦。显然,如果库没有上游源代码,那么没有理由不走这里的重构之路。