我非常喜欢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
而h
等于Hex::n15
会将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;
}
类型!这个问题在其他枚举中更为明显:
int
这个问题对我来说是一个惊喜;我打赌将不正确的值存储到枚举值中会引发异常。所以,现在我想知道是否有一种优雅的方式来处理这个枚举的弱点,我想要实现的目标是:
其他问题:
Hex
和Prime
中的{{1}}类型。答案 0 :(得分:4)
正如您所注意到的,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
无处不在。
(FWIW:我的代码生成器不会生成++
,--
和
迭代器,如果任何枚举值具有显式
定义的值,除非全部,否则不会生成|
,&
等
值已明确定义值。)
为什么在外部转换某些值时没有错误
法律范围(例如100,对于X
,上面)只是保持不变
从C继承的一般哲学:最好是
快于正确。做额外的范围检查会
需要额外的运行时成本。
最后关于你的最后一个例子:我不认为这是
真实地使用enum
。这里正确的解决方案是
int[]
。虽然C ++ enum
是一个混合品种,但我愿意
仅将其用作实际枚举类型,或用于位掩码(和
仅用于位掩码,因为它是如此广泛建立的
成语)。
答案 1 :(得分:1)
您可以使用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)
)。