Consider the following piece of code:
#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;
}
When I try to compile this (gcc 5.4.0) I get the following error:
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()};
I don't really understand why there is any narrowing at all. The int type is never used anywhere in my code, everything is written in terms of unsigned chars. Even if I explicitly cast to unsigned char I get the error:
uint8_t mask{static_cast<uint8_t>(a.get_a()) | static_cast<uint8_t>(aa.get_a())};
In fact, to solve this, I need to remove the {}-initialization. Then it works:
uint8_t mask = a.get_a() | aa.get_a();
Why is this necessary?
You were close with this:
uint8_t mask{static_cast<uint8_t>(a.get_a()) | static_cast<uint8_t>(aa.get_a())};
But a.get_a() and aa.get_a() are already uint8_t, so the cast does nothing.
It's the | operation that:
int (after you can do anything about it)int
So it's the whole expression you now need to subsequently convert:
uint8_t mask{static_cast<uint8_t>(a.get_a() | aa.get_a())};
You were also right to try dropping the {}-initialisation, which is probably what I'd do too. You just don't need its strictness here.
uint8_t mask = a.get_a() | aa.get_a();
This is clear, concise and correct.
Most binary arithmetic operations including the | bitwise-or that appears here force their subexpressions to be promoted, which is to say they will be at least int or unsigned int in rank.
C++ 17 [expr] paragraph 11:
Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions, which are defined as follows:
If either operand is of scoped enumeration type, ...
If either operand is of type
long double, ...Otherwise, if either operand is
double, ...Otherwise, if either operand is
float, ...Otherwise, the integral promotions shall be performed on both operands. Then the following rules shall be applied to the promoted operands: ...
The integral promotions here are what cause the get_a() values to change from uint8_t to int. So the result of the | expression is also an int, and narrowing it to initialize another uint8_t is ill-formed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With