【发布时间】:2019-06-15 04:40:10
【问题描述】:
我正在阅读 C++ 标准 n4713.pdf。考虑下面的代码:
#include <iostream>
#include <type_traits>
enum UEn
{
EN_0,
EN_1,
EN_L = 0x7FFFFFFFFFFFFFFF // EN_L has type "long int"
}; // UEn has underlying type "unsigned long int"
int main()
{
long lng = 0x7FFFFFFFFFFFFFFF;
std::cout << std::boolalpha;
std::cout << "typeof(unsigned long == UEn):" << std::is_same<unsigned long, std::underlying_type<UEn>::type>::value << std::endl; // Outputs "true"
std::cout << "sizeof(EN_L):" << sizeof(EN_L) << std::endl;
std::cout << "sizeof(unsigned):" << sizeof(unsigned) << std::endl;
std::cout << "sizeof(unsigned long):" << sizeof(unsigned long) << std::endl;
std::cout << "sizeof(unsigned long):" << sizeof(unsigned long long) << std::endl;
lng = EN_L + 1; // Invokes UB as EN_L is 0x7FFFFFFFFFFFFFFF and has type "long int"
return 0;
}
以上代码输出(在g++-8.1,Clang上测试):
typeof(unsigned long == UEn):truesizeof(EN_L):8sizeof(unsigned):4sizeof(unsigned long):8sizeof(unsigned long):8
根据第 10.2p5 节(10.2 枚举声明):
在枚举说明符的右大括号之后,每个枚举数都有 其枚举的类型...如果基础类型不固定,则 右大括号之前的每个枚举数的类型被确定为 如下:
如果为枚举数指定了初始值设定项,则 常量表达式应该是一个整数常量表达式(8.6)。如果 表达式具有无范围的枚举类型,枚举数具有 该枚举类型的基础类型,否则它具有相同的 输入为表达式。
如果没有为第一个指定初始化器 枚举器,它的类型是一个未指定的有符号整数类型。
否则枚举器的类型与枚举器的类型相同 前面的枚举器,除非增量值不可表示 在该类型中,在这种情况下,该类型是未指定的整数类型 足以包含增加的值。如果不存在这样的类型, 程序格式不正确。
此外,第 10.2p7 节指出:
对于底层类型不固定的枚举,底层 type 是一个整数类型,可以表示所有枚举数值 在枚举中定义。如果没有整数类型可以代表所有 枚举值,枚举格式不正确。它是 实现定义使用哪种整数类型作为底层 类型,但基础类型不得大于 int 除非枚举器的值不能放入 int 或 unsigned 诠释。
因此我有以下问题:
- 当
0x7FFFFFFFFFFFFFFF是long int类型的整数常量,因此EN_L的类型也是long int时,为什么枚举的基础类型UEn是unsigned long。这是编译器错误还是定义明确的行为? - 当标准说
each enumerator has the type of its enumeration时,不应该暗示枚举器和枚举的整数类型也应该匹配吗?这两者不同的原因可能是什么?
【问题讨论】:
-
0x7FFFFFFFFFFFFFFF 是不确定符号的整数常数。您的编译器选择将其存储为
unsigned long。 -
@JonHarper:标准规定:“整数文字的类型是表 7 中对应列表的第一个,其中可以表示其值:二进制、八进制或十六进制文字- int, unsigned int, long int, unsigned long int, long long int, unsigned long long int”。 编译器不能随心所欲,不是吗?
-
符合标准的编译器可以在某些情况下(UB,未指定的行为)随心所欲地采取行动,但否则你是正确的。然而,编译器很少(如果有的话)真正符合标准。