在 { } | 中缩小从 int 到 char 的转换char 的签名问题

Narrowing Conversion from int to char inside { } | An issue with signedness of char

查看 charvector<char> 和独立 char 情况下的行为:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
    vector<int> someInts = {2, 3, 1 + 5};
    // I can perform operations   ^^^^^   inside the braces in this case

    // vector<char> someChars = {'a', 'b', 'r' + 'z'};
    // But doing the same in case of char  ^^^^^^^^^  gives an error

    // However, it works for a standalone char
    char ch = 'r' + 'z';
    cout << ch;
    // Value^^^is '∞' as expected
}

取消注释 vector<char> 行给出:

Error: narrowing conversion of '236' from 'int' to 'char' inside { }

这就是问题所在。


然后我阅读了 List Initializationthis 文档,并看到以下可能与此问题相关的内容:

Narrowing conversions

list-initialization limits the allowed implicit conversions by prohibiting the following:

  • many other reasons
  • conversion from integer or unscoped enumeration type to integer type that cannot represent all values of the original, except where source is a constant expression whose value can be stored exactly in the target type

这让我理解了错误消息(我猜),我将 vector<char> 更改为 vector<unsigned char> 并且成功了:

vector<unsigned char> someChars = {'a', 'b', 'r' + 'z'};
for (char ch : someChars)
    cout << '_' << ch;

Output: _a_b_∞


那么,我的问题是:

  1. 如果 charsignedness 是问题所在,那么 char 的独立版本在那种情况下是如何工作的?参考Whosebug上的this线程,同一个编译器如何为vector<char>选择signed char和为char选择unsigned char

  2. 如果我对问题的推导是错误的,那么这个问题背后的正确原因是什么?

1+5 是一个 int,因此使用它初始化 vector<int> 条目没有问题。

'r' + 's' 是一个 int (236),它超出了您系统上 char 的范围。所以 一个问题,试图用它来初始化一个 vector<char> 条目。这正是关于列表初始化不允许缩小转换的规则设计的那种情况。

你会得到与 vector<char> x = { 'a', 'b', 123456 };

相同的错误

独立 char 具有与 signed charunsigned char 相同的属性,它是实现定义的,一些编译器有一个开关可供选择(例如 -funsigned-char 在 GCC 上)。需要明确的是,在这两种情况下它仍然是不同的类型。


用你的话来说, char 确实在这两种情况下都已签署,正如 Nathan Oliver 在评论中所解释的那样。问题是您错误地认为独立 char 在您的情况下未签名。仅仅因为 char ch = 'r' + 'z'; compiled 并不表明它是未签名的。您可能认为它是无符号的,因为 'r' + 'z' == 236 不适合 signed char。不,它确实适合 signed char,原因如下:

char ch = 'r' + 'z'; 超出范围的转换 。由于 C++20,结果将是 -20(假设普通 char 是 8 位签名的,如您之前的错误消息所示,并且系统使用 ASCII 编码),在此之前结果是实现定义的。


所以,不要假设事情。如果您不清楚 char 在您的系统上是有符号的还是无符号的,只需检查 CHAR_MIN.