在循环中使用enum和值一致性
Using enum in loops and value consistency
我是c++强类型特性的忠实粉丝,我最喜欢的是在处理有限的数据集时使用枚举。
但是枚举缺少一些有用的特性,例如操作符:
enum class Hex : int
{
n00, n01, n02, n03,
n04, n05, n06, n07,
n08, n09, n10, n11,
n12, n13, n14, n15
};
for (Hex h = Hex::n0; h <= Hex::n15; ++h) // Oops! no 'operator ++'
{ /* ... */ }
很容易避免缺少操作符,在同一作用域中创建自由操作符:
Hex &operator ++(Hex &h)
{
int r = static_cast<int>(Hex);
h = static_cast<Hex>(r + 1);
return h;
}
for (Hex h = Hex::n0; h <= Hex::n15; ++h) // Now the '++h' works!
{
std::cout << std::dec << int(h) << ": "
<< std::hex << int(h) << 'n';
}
但是这种方法更麻烦,而不是解决问题,因为它可以打破枚举的值限制:当h
等于Hex::n15
时,应用++h
将使h的值为16,这超出了Hex
的值范围,而h
仍然是Hex
的类型,这个问题在其他枚举中更明显:
enum class Prime : int
{
n00 = 2, n01 = 3, n02 = 5, n03 = 7,
n04 = 11, n05 = 13, n06 = 17, n07 = 19,
n08 = 23, n09 = 29, n10 = 31, n11 = 37,
n12 = 41, n13 = 43, n14 = 47, n15 = 53
};
Prime &operator ++(Prime &p)
{
// How to implement this?! will I need a lookup table?
return p;
}
这个问题对我来说是个惊喜;我打赌,将不正确的值存储到枚举值中会引发异常。所以,现在我想知道是否有一种优雅的方法来处理这个枚举的弱点,我想实现的目标是:
- 找到一种在循环中使用枚举值的舒适方式。
- 确保操作间枚举数据的一致性。
额外的问题:
- 当枚举数据获得超出其可能值的值时,是否有理由不抛出异常? 有一种方法来推断与枚举类相关联的类型?,枚举
Hex
和Prime
中的int
类型。正如你所注意到的,c++中的enum
是而不是枚举类型,但更复杂(或更混合)的东西。当你定义enum
,您实际上定义了两件事:
-
整型,其合法范围足以包含或者所有枚举值的。(技术上说:范围是
2^n - 1
,其中n
是所需的位数 -
一系列具有新定义类型的命名常量。
(我不确定会发生什么关于范围,如果你显式指定底层类型)
例如,给定enum Prime
,合法值将为[0...64)
范围内的所有整数,即使所有这些值没有名称。(至少如果你没有明确地说说它应该是一个int
。)
可以不使用实现枚举的迭代器初始化;我有一个程序生成必要的代码。但它的工作原理是将值保持为整型它足够大,可以包含最大值+ 1。我的机器在这样的枚举上生成++
的实现将assert
,如果你试图增加超出结束。(注意,您的第一个示例将需要迭代h
最后一个值:我实现的各种操作符没有允许这样做,这就是我使用迭代器的原因。)
关于为什么c++支持扩展范围:enum
经常被使用定义位掩码:
enum X
{
a = 0x01,
b = 0x02,
c = 0x04,
all = a | b | c,
none = 0
};
X operator|( X lhs, X rhs )
{
return X((int)lhs | (int)rhs);
}
// similarly, &, |= and &=, and maybe ~
有人可能会说,这种用法最好由一个类,但enum
的使用是普遍的。
(fww:我的代码生成器不会生成++
, --
和枚举值中任何一个具有显式的定义的值,并且不会生成|
, &
等,除非全部这些值有明确定义的值)
关于为什么在外部转换某些值时没有错误合法的范围(例如100,对于X
,上面)只是保持不变带着从C那里继承来的一般哲学:生活总比生活好比正确还要快。做额外的范围检查需要额外的运行时成本。
enum
的实际使用。正确的解是anint[]
。虽然c++ enum
是一个相当复杂的品种,但我愿意仅将其用作真正的枚举类型,或用于位掩码(和只适用于位掩码,因为它是如此广泛建立成语)。 您可以使用switch
:
class Invalid {};
Prime& operator ++(Prime& p)
{
switch(p)
{
case n00: return n01;
case n01: return n02;
case n02: return n03;
case n03: return n04;
case n04: return n05;
case n05: return n06;
case n06: return n07;
case n07: return n08;
case n08: return n09;
case n09: return n10;
case n10: return n11;
case n11: return n12;
case n12: return n13;
case n13: return n14;
case n14: return n15;
// Here: 2 choices: loop or throw (which is the only way to signal an error here)
case n15: default: throw Invalid();
}
}
但请注意,这是而不是对枚举的正确使用。我个人认为这很容易出错。如果要枚举整数,可以使用整数数组,或者对于素数,可以使用函数(在数学意义上:int nextPrime(int)
)。
相关文章:
- enum是C++中的宏变量还是整数变量
- 是否可以从int转换为enum类类型
- 在一个模板函数中,若输入的类型是enum类,我该如何使用std::underlying_type
- 缓存std::数组的选定元素,并在c++中自动保持其一致性
- 在C++中,将int值赋给enum,反之亦然
- 两个 COM 组件中 ENUM 的重复条目
- 类继承,ENUM 与 AST 类实现的问题
- 标准::make_pair 和标准::make_optional之间的一致性
- C++关于ENUM的问题。我得到的响应比枚举列表大
- 可以从std::string继承以提供类型一致性吗
- 标记为 std::memory_order_seq_cst 的单个原子操作是否会在所有位置触发顺序一致性?
- 顺序一致性和获取/发布语义有什么区别?
- 用于验证 Visual Studio 一致性开关对生成的代码的影响的工具
- sizeof(enum) 可以不同于 sizeof(std::underlying_type<Enum>::
- 如何理解c++中在命名空间内部定义的枚举类型enum
- 如何将set和enum一起使用
- 如何限制从int到enum类的转换
- 编译为 cuda 内核调用提供了"expression must have integral or unscoped enum type"
- Access C++ Enum from Swift
- 在循环中使用enum和值一致性