return 位域访问类型
return type of access to bitfield
#include <iostream>
#include <type_traits>
struct C
{
uint32_t x : 2;
bool y : 2;
};
int main()
{
C c{0b1};
std::cout << (static_cast<uint32_t>(0b1) << 31) << std::endl;
std::cout << (c.x << 31) << std::endl;
std::cout << (c.x << 10) << std::endl;
std::cout << std::boolalpha << std::is_same_v<decltype(c.x), uint32_t> << std::endl;
std::cout << std::boolalpha << std::is_same_v<decltype(c.y), bool> << std::endl;
}
编译
g++ -g test.cpp -std=c++17
g++ (GCC) 8.2.0
输出
2147483648
-2147483648
1024
true
true
我的问题是关于表达式 c.x
的类型,其中 x
是一个 2 位位域成员。根据 typetraits 检查,我得到的类型与 class 定义中声明的类型相同,但是在运行时这似乎不是真的,因为当我尝试通过移位设置最后一位时,我得到了一个负数。有什么想法吗?
来自 C++draft 2019-04-12 conv.prom 7.3.6p5:
7.3.6 Integral promotions
A prvalue for an integral bit-field ([class.bit]) can be converted to a prvalue of type int if int can represent all the values of the bit-field;
来自 C++draft 2019-04-12 expr.shift 7.6.7p1:
7.6.7 Shift operators
The shift operators << and >> group left-to-right.
...
The operands shall be of integral or unscoped enumeration type and integral promotions are performed.
typeid(c.x)
是 uint32_t
,但是当使用 <<
运算符时,它被隐式转换为 int
.
c.x
是 0x1
。表达式 c.x << 31
是 0x1 << 31
是 0x80000000
(假设 sizoef(int) == 4
和 CHAR_BIT == 8
)。此数字被解释为 int
,在 twos complement 格式中它等于 -2147483648
(INT_MIN
或 std::intergral_limits<int>::min()
)。
请注意,表达式 c.x << 31
当前 (C++17) 由于有符号整数溢出而调用未定义的行为。
Moreover what is the significance of the type declared in the class definition then?
填充。一些编译器将不同类型的位域解释为“填充分隔符”(不知道如何命名)。如果结构中的下一个成员的类型与前一个成员的类型不同(都是位域),那么我希望编译器将第二个成员从一个新的“新”字节开始放置。我希望 c.x
和 c.y
在它们之间有位填充,因为它们有不同的类型。如果它是 struct C { uint32_t y : 2; uint32_t x : 2; }
那么编译器更有可能将它们放在同一个字节中。请参阅您的编译器文档或 other resources.
#include <iostream>
#include <type_traits>
struct C
{
uint32_t x : 2;
bool y : 2;
};
int main()
{
C c{0b1};
std::cout << (static_cast<uint32_t>(0b1) << 31) << std::endl;
std::cout << (c.x << 31) << std::endl;
std::cout << (c.x << 10) << std::endl;
std::cout << std::boolalpha << std::is_same_v<decltype(c.x), uint32_t> << std::endl;
std::cout << std::boolalpha << std::is_same_v<decltype(c.y), bool> << std::endl;
}
编译
g++ -g test.cpp -std=c++17
g++ (GCC) 8.2.0
输出
2147483648
-2147483648
1024
true
true
我的问题是关于表达式 c.x
的类型,其中 x
是一个 2 位位域成员。根据 typetraits 检查,我得到的类型与 class 定义中声明的类型相同,但是在运行时这似乎不是真的,因为当我尝试通过移位设置最后一位时,我得到了一个负数。有什么想法吗?
来自 C++draft 2019-04-12 conv.prom 7.3.6p5:
7.3.6 Integral promotions
A prvalue for an integral bit-field ([class.bit]) can be converted to a prvalue of type int if int can represent all the values of the bit-field;
来自 C++draft 2019-04-12 expr.shift 7.6.7p1:
7.6.7 Shift operators
The shift operators << and >> group left-to-right.
...
The operands shall be of integral or unscoped enumeration type and integral promotions are performed.
typeid(c.x)
是 uint32_t
,但是当使用 <<
运算符时,它被隐式转换为 int
.
c.x
是 0x1
。表达式 c.x << 31
是 0x1 << 31
是 0x80000000
(假设 sizoef(int) == 4
和 CHAR_BIT == 8
)。此数字被解释为 int
,在 twos complement 格式中它等于 -2147483648
(INT_MIN
或 std::intergral_limits<int>::min()
)。
请注意,表达式 c.x << 31
当前 (C++17) 由于有符号整数溢出而调用未定义的行为。
Moreover what is the significance of the type declared in the class definition then?
填充。一些编译器将不同类型的位域解释为“填充分隔符”(不知道如何命名)。如果结构中的下一个成员的类型与前一个成员的类型不同(都是位域),那么我希望编译器将第二个成员从一个新的“新”字节开始放置。我希望 c.x
和 c.y
在它们之间有位填充,因为它们有不同的类型。如果它是 struct C { uint32_t y : 2; uint32_t x : 2; }
那么编译器更有可能将它们放在同一个字节中。请参阅您的编译器文档或 other resources.