所有的C++枚举类型都至少有两个合法值吗?

12

在使用一些自定义比较器时,我遇到了需要一个仅具有单个可能值的类型的情况。存在像std::nullptr_t和空结构体这样的类型,这种情况就是如此。

然后我考虑使用枚举。我可以声明一个只有一个值的枚举,例如


```cpp enum class MyEnum : int { OnlyValue = 0 }; ```
enum E
{
  only_value // BUT IS IT??
}; 

但是,标准似乎规定了所有符合声明值的“最小位域”所包含底层类型的值都是有效的。

来自cppreference.com

(如果将源值转换为枚举基础类型(如果是浮点数),则该值在范围内,如果它适合容纳目标枚举中的所有枚举器的最小位域中。)

如果您只使用一种枚举器声明枚举,则最小大小可以为1个位。按照这个逻辑,另一个值的未命名枚举器应该是合法的。如果枚举器基于有符号整数,则始终可以使用-10。在无符号整数上,01始终合法。

标准中是否还有其他使未命名比特值非法或UB(未定义行为)的内容?


3
我不是语言专家,但对于我来说,如果要存储只有一个值的枚举类型,你需要的位数是0,而不是1(前提是该值为0)。 - prapin
@prapin 或许吧...但 nullptr 占用空间,所以我猜枚举类型也会占用空间。我想知道是否有漏洞可以允许它被优化掉。 - Spencer
1
@prapin 这样想:enum ultimate { answer=42 }; 总是占用 7 位,而从 -64 到 63 的所有值都是合法的。 - Spencer
@Spencer,enum ultimate { answer = 42 }; 如何占用7位?在二进制中,数字42表示为101010,需要6位。在8位机器上,它将占用8位,因为8位是最小可寻址大小。我感到困惑。 - Thomas Matthews
1
@ThomasMatthews 因为底层类型是有符号的。6位可以容纳42加上一个符号位。"位域"可以比8位小。 - Spencer
2个回答

5
看起来enum E { value = 0; }只有一个值,即为0。标准如下所述:
[dcl.enum]/8 ...否则[如果底层类型未固定 - IT],对于枚举类型,其中e min是最小的枚举器,e max是最大的枚举器,则枚举的值是范围b min到b max中的值, 定义如下:设K为表示二进制补码的1,表示反码或补码表示的0. b max是大于或等于max(|e min| − K, |e maxn|)的最小值,等于2 M - 1,其中M是非负整数。如果e min为非负,则b min为零,否则为 -(b max + K)
e min == e max == 0时,根据我的计算,这个公式也会产生b min == b max == 0 但是,在您抱有希望之前,请参见脚注96:
这组值用于定义枚举类型的提升和转换语义。它不排除枚举类型的表达式具有超出此范围的值。
我必须承认,我并不立刻明白如何生成超出该范围的枚举类型的表达式。至少,将枚举类型的静态转换显示为未定义行为,如果被转换的值超出了范围。

那么,cppreference关于位域大小定义范围的断言是不正确的吗? - Spencer
1
@Spencer,这个答案只涉及到 C++(C++17 及以下版本)的补码。C++20+ 则是以整数宽度为术语表述的。https://timsong-cpp.github.io/cppwp/n4868/dcl.enum#8.sentence-2 - Language Lawyer
Igor,请查看@LanguageLawyer的评论。在你解决C++版本特定性之前,这个答案不会完整。 - Spencer

1

@Igor的回答在旧版(C++17及更早版本)的C++中是正确的。
我很困惑他所提出的标准语言与cppreference所显示的不符,直到@LanguageLawyer在评论中指出Igor正在使用旧版的标准。

在C++20及以后的版本中,语言是不同的(https://timsong-cpp.github.io/cppwp/n4868/dcl.enum#8.sentence-2)。

有趣的是,

对于底层类型固定的枚举,枚举的值就是底层类型的值。

这意味着,如果一个枚举类型已经指定了其类型,那么该枚举类型允许使用底层类型的全部取值范围!这使得基于无符号类型的枚举“刚好合适”变得不可能。

但更重要的是我的问题:

否则,枚举的值是由一个假想的整数类型表示的值,该整数类型具有最小宽度M,以便可以表示所有枚举器。足以容纳枚举类型所有值的最小位域的宽度为M。可以定义一个枚举,其值未被任何枚举器定义。
因此,鉴于上述情况。
enum E
{
  dummy = 0
};

最小的位域是1位,它必须有两个值,另一个值是-1(因为C++中默认的基础类型是int)。

在标准中接下来的句子是:

如果枚举列表为空,则枚举的值就像枚举具有单个值0一样。

起初这可能令人担忧,直到我们意识到标准将“枚举器”(在枚举声明内部的名称)和“值”区分开来。这只是意味着

enum E0 { };

具有隐式枚举器,例如如果声明了相同的内容

enum E0 { dummy = 0 };

它还有1位可用,并允许-1值。


@LanguageLawyer 这看起来对吗? - Spencer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接