考虑以下代码:
git diff ORIG_HEAD
当我尝试编译它(#include <cstdint>
class A
{
public:
explicit A(uint8_t p_a){ m_a = p_a; };
uint8_t get_a() const {return m_a;}
private:
uint8_t m_a;
};
int main()
{
A a {0x21U};
A aa{0x55U};
uint8_t mask{a.get_a() | aa.get_a()};
return 0;
}
)时,我收到以下错误:
gcc 5.4.0
我真的不明白为什么会有任何缩小。 main.cpp: In function ‘int main()’:
main.cpp:20:28: warning: narrowing conversion of ‘(int)(a.A::get_a() | aa.A::get_a())’ from ‘int’ to ‘uint8_t {aka unsigned char}’ inside { } [-Wnarrowing]
uint8_t mask{a.get_a() | aa.get_a()};
类型从未在我的代码中的任何位置使用,所有内容都是根据int
s来编写的。即使我明确地转向unsigned char
,我也会收到错误:
unsigned char
实际上,要解决这个问题,我需要删除uint8_t mask{static_cast<uint8_t>(a.get_a()) | static_cast<uint8_t>(aa.get_a())};
- 初始化。然后它工作:
{}
为什么这有必要?
答案 0 :(得分:7)
你很接近这个:
uint8_t mask{static_cast<uint8_t>(a.get_a()) | static_cast<uint8_t>(aa.get_a())};
但是a.get_a()
和aa.get_a()
已经uint8_t
,所以演员没有做任何事情。
这是|
操作:
int
(在您可以执行任何操作之后)int
所以这是你现在需要随后转换的整个表达式:
uint8_t mask{static_cast<uint8_t>(a.get_a() | aa.get_a())};
您也可以尝试删除{}
- 初始化,这可能也是我所做的。你这里不需要严格。
uint8_t mask = a.get_a() | aa.get_a();
这是清楚,简洁和正确的。
答案 1 :(得分:3)
大多数二进制算术运算,包括|
按位 - 或出现在这里强制它们的子表达式被提升,也就是说它们的排名至少为int
或unsigned int
。
C ++ 17 [expr]第11段:
许多期望算术或枚举类型操作数的二元运算符会以类似的方式导致转换并产生结果类型。目的是产生一个通用类型,它也是结果的类型。此模式称为通常的算术转换,其定义如下:
如果任一操作数是作用域枚举类型,...
如果任一操作数的类型为
long double
,...否则,如果任一操作数为
double
,...否则,如果任一操作数为
float
,...否则,应对两个操作数执行整体促销。然后,以下规则应适用于提升的操作数:...
此处的integral promotions是导致get_a()
值从uint8_t
更改为int
的原因。因此,|
表达式的结果也是int
,缩小它以初始化另一个uint8_t
的结果不正确。
答案 2 :(得分:1)
prvalues 小积分类型(例如
char
)可以转换为 较大整数类型的prvalues(例如int
)。
a.get_a() | aa.get_a()
- 这是prvalue表达式
答案 3 :(得分:1)
列表初始化更严格,这就是您收到警告的原因。
uint8_t mask{a.get_a() | aa.get_a()};
大括号内的表达
auto i = a.get_a() | aa.get_a(); // i is int
升级为int
,由于int
无法完全符合uint8_t
,因此会根据this rule发出缩小警告:
如果initializer子句是表达式,则隐式转换为 允许按照复制初始化,除非它们正在缩小(如 在列表初始化中)(从C ++ 11开始)。