在循环中使用枚举和值一致性

时间:2012-12-20 11:47:22

标签: c++ c++11 enums

我非常喜欢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';
}

但这种方法比解决方案更令人讨厌,因为它可以打破枚举的价值限制:应用++hh等于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

这个问题对我来说是一个惊喜;我打赌将不正确的值存储到枚举值中会引发异常。所以,现在我想知道是否有一种优雅的方式来处理这个枚举的弱点,我想要实现的目标是:

  • 找到一种在循环中使用枚举值的舒适方法。
  • 确保操作之间的enumation数据一致性。

其他问题:

  • 如果枚举数据的值超出其可能的值,是否有理由不抛出异常?
  • 有一种方法可以推断出与枚举类相关联的类型?枚举HexPrime中的{{1}}类型。

2 个答案:

答案 0 :(得分:4)

正如您所注意到的,C ++中的enum 不是枚举类型, 但更复杂(或更复杂)的东西。当你定义一个 enum,你实际上定义了两件事:

  1. 具有足以包含的合法范围的整数类型 或所有枚举值。 (技术上:范围 是2^n - 1,其中n是必需的位数 保持最大值。)

  2. 一系列具有新定义类型的命名常量。

  3. (我不确定如果你这个范围会发生什么 明确指定基础类型。)

    例如,鉴于您的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))。