在 { } | 中缩小从 int 到 char 的转换char 的签名问题
Narrowing Conversion from int to char inside { } | An issue with signedness of char
查看 char
在 vector<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 Initialization 的 this 文档,并看到以下可能与此问题相关的内容:
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_∞
那么,我的问题是:
如果 char
的 signedness 是问题所在,那么 char
的独立版本在那种情况下是如何工作的?参考Whosebug上的this线程,同一个编译器如何为vector<char>
选择signed char
和为char
选择unsigned char
?
如果我对问题的推导是错误的,那么这个问题背后的正确原因是什么?
1+5
是一个 int
,因此使用它初始化 vector<int>
条目没有问题。
'r' + 's'
是一个 int
(236),它超出了您系统上 char
的范围。所以 是 一个问题,试图用它来初始化一个 vector<char>
条目。这正是关于列表初始化不允许缩小转换的规则设计的那种情况。
你会得到与 vector<char> x = { 'a', 'b', 123456 };
相同的错误
独立 char
具有与 signed char
或 unsigned 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
.
查看 char
在 vector<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 Initialization 的 this 文档,并看到以下可能与此问题相关的内容:
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_∞
那么,我的问题是:
如果
char
的 signedness 是问题所在,那么char
的独立版本在那种情况下是如何工作的?参考Whosebug上的this线程,同一个编译器如何为vector<char>
选择signed char
和为char
选择unsigned char
?如果我对问题的推导是错误的,那么这个问题背后的正确原因是什么?
1+5
是一个 int
,因此使用它初始化 vector<int>
条目没有问题。
'r' + 's'
是一个 int
(236),它超出了您系统上 char
的范围。所以 是 一个问题,试图用它来初始化一个 vector<char>
条目。这正是关于列表初始化不允许缩小转换的规则设计的那种情况。
你会得到与 vector<char> x = { 'a', 'b', 123456 };
独立 char
具有与 signed char
或 unsigned 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
.