【问题标题】:How to make a enum with values over max int allowed?如何制作一个允许值超过最大 int 的枚举?
【发布时间】:2012-11-14 19:18:14
【问题描述】:

我正在 C++ 中创建一个枚举,以使用二进制标志创建一个有限状态机。它看起来像:

enum VStates
{
    NEUTRAL         =   0x00000000,     // 000000
    //  Physical Status
    DRY             =   0x00000001,     // 000001
    WET             =   0x00000002,     // 000010
    HOT             =   0x00000004,     // 000100
    COLD            =   0x00000008,     // 001000
    BURNED          =   0x00000016,     // etc..
    FROZEN          =   0x00000032,
    EROS            =   0x00000064,     // 
    THANATOS        =   0x00000128,     // 
    SLEEP           =   0x00000256,
    STUNNED         =   0x00000512,
    PARALYZED       =   0x00001024,
    POISONED        =   0x00002048,     //
    BLIND           =   0x00004096,
    SOFT            =   0x00008192,     // Flexible
    TOUGH           =   0x00016384,     // Resistent
    MAGNETIZED      =   0x00032768,
    POSSEDERUNT     =   0x00131072,     //
    // Mental Status
    ANGRY           =   0x00262144,
    DRUGGED         =   0x00524288, // Drugs Meaning
    HORNY           =   0x01048576, // Sexual Meaning
    // Material Status
    METAL           =   0x02097152,
    WOOD            =   0x04194304,
    GLASS           =   0x08388608,
    AIR             =   0x16777216,
    EARTH           =   0x33554432,
    DUST            =   0x67108864,
    LIGHT           =   0x134217728,
    SHADOW          =   0x268435456,
    WATER           =   0x536870912,
    // Total Status
    PROTECTED       =   0x1073741824,
    INVULNERABLE    =   0x2147483648

};

有些状态是不兼容的,所以我使用按位运算符来管理它们。现在,我的编译器说:

warning: integer constant is too large for 'long' type

这是声明此枚举的正确方法吗?我喜欢避免警告,我该如何解决这个问题?

【问题讨论】:

  • 不是一个答案,但如果将值写为十六进制常量,这将更容易写入和读取。
  • @PeteBecker:它们实际上写成十六进制常量——但看起来他正在使用他应该使用十进制常量的数字(例如,他有 0x00000512,这真的应该为 512 或 0x200)。
  • 您知道0x00000016 不是16 岁吗?如果它们用十进制而不是十六进制常量初始化,它们可能适合您的枚举。

标签: c++ variables enums


【解决方案1】:

在 C++11 中,可以指定枚举的底层类型。

#include <cstdint>

enum VStates : uint64_t {
    // Values
}

顺便说一句,我建议不要计算所有这些 2 的幂。您通过写入一个十六进制常量但给它一个以 10 为基数的数字,从而在计算中出错。但是,我建议不要重新计算所有这些,而是​​:

#include <cstdint>

enum VStates : uint64_t {
    NEUTRAL = 0ULL,
    DRY = 1ULL << 0,
    WET = 1ULL << 1,
    HOT = 1ULL << 2,
    COLD = 1ULL << 3,
    // etc.
}

那你肯定不会犯错。 ULL 后缀确保字面量被接受为至少 64 位宽的整数。

【讨论】:

    【解决方案2】:

    (注意:为了使我的答案完整,我将添加一些我没有花时间注意到但其他人指出的内容:您使用的是 0x 前缀,这意味着您的号码将是解释为十六进制。它们实际上不是 2 的幂,并且您的 bitflag 测试不会起作用!)

    如果您的枚举像这样失控,请不要使用枚举类型。使用类似std::bitset 的东西。那么你的枚举可以只是一个简单的编号列表,用于表示集合中位的位置......而且你不会以指数方式耗尽你的枚举空间!

    例如:

    enum VState {
        NEUTRAL,
        DRY,
        WET,
        COLD,
        BURNED,
        FROZEN,
        /* ... */
        VState_Max
    };
    
    bitset<VState_Max> state;
    
    state[COLD] = true;
    if (state[COLD]) {
        cout << "I am cold\n";
    }
    

    现在您的枚举只是很小的、可维护的数字,您不必担心在 64 位平台或其他平台上。

    我注意到您在原始示例中为 NEUTRAL 提供了“0”值。如果您的意图是可以将它与其他东西结合使用......例如能够成为state = NEUTRAL | INVULNERABLE | SHADOW 并单独测试NEUTRAL,那以前是行不通的。现在它将...您只需将其保留在用于索引位集的枚举中。

    但是如果它打算作为“无设置”的名称,那么您将从枚举中删除它,而是测试没有设置的位:

    if (state.none()) {
        // we are in the "NEUTRAL" state of nothing set...
    }
    

    ...如果您想将所有位设置为 false,您可以选择:

    state.reset();
    

    【讨论】:

    • 这是一个比我更好的答案,因为您纠正了真正的潜在问题。对标志集合使用std::bitset,而不是枚举。比使用枚举索引更好的方法是将其包装在一个类中,该类将std::bitset 作为具有命名成员访问函数的私有成员。 class VStates { public: bool is_dry() const { return bits[1]; } ... };
    • @DavidStone 谢谢。如果这些观众对包装信息感兴趣,那么我的Nstate library 可能会感兴趣...
    • 我可能最终会使用它。我目前正在开发一个应用程序,其中大约 2/3 的时间用于复制数据。我的相关数据结构的大小减少 50% 对应于运行时间减少略低于 30%,而且似乎我的应用程序的至少一部分可能适合该解决方案。
    • @DavidStone 我考虑将它与the NoCycle project 分开打包,因为它的构建依赖关系更简单,应用程序更常见。将 NState.hpp 和 NState.cpp 文件切出来尝试一下非常容易,但是如果有人认真使用它,请告诉我,我会对其进行标准化。需要重新审视 C++11 的内容,例如移动构造 / 等。
    • 有趣的是,在这篇文章发布后的一两天内,我确实有机会使用它。我最终将我的评论和您的答案结合起来。我创建了一个包含enum { value0, value1, value2, ..., END } 的类。该类有一个std::bitset&lt;Enum::END&gt; 成员,我使用成员函数is_set(Enum e) const 访问这些值。这让我可以确定,无论我如何更改合法值的数量,我总是会索引正确的位置。这似乎是两全其美。
    【解决方案3】:

    枚举有 31 个非零值,因此它们都可以放入 32 位无符号值。问题是这里的值不是位值。要么写成十进制值(去掉前面的0x),要么写成十六进制值(0x01、0x02、0x04、0x08、0x10、0x20、0x40等)我个人不喜欢,但有些人们用移位写出这种常量:1

    【讨论】:

    • 我很好奇你不喜欢轮班吗?它们不太容易出错,快速浏览一下枚举,您就会立即知道可以对其使用按位运算。
    • @Joe - 这纯粹是审美。它们并不比十六进制值更容易出错,而且我认为它们很丑。
    【解决方案4】:

    如果你使用 C++11,你可以声明一个强类型枚举,其定义类型为unsigned long long(在 Windows 中为_int64,尽管你可能应该使用可移植的uint64_t),如果它扩展你的射程够远。

    感谢 Joachim 提供 C++11 枚举使用示例的链接:Strongly Typed Enums

    【讨论】:

    • 参见例如this Wikipedia link 关于强类型枚举,以及如何定义它们。具有不同的整数类型。
    • @JoachimPileborg 谢谢,介意我使用该链接来改进答案吗?
    【解决方案5】:

    使用较小的数字。枚举只能和 long 一样大。 long 的大小取决于编译器,但典型的大小是 32 位或 64 位。我在那里看到一些 10 位的十六进制数字,它们太大了。

    【讨论】:

      猜你喜欢
      • 2018-11-16
      • 1970-01-01
      • 2015-05-20
      • 1970-01-01
      • 2021-05-19
      • 2014-09-02
      • 1970-01-01
      • 1970-01-01
      • 2016-11-12
      相关资源
      最近更新 更多