C ++枚举是否可以大于64位?

时间:2014-03-14 01:24:21

标签: c++ c++11 enums

在我的entity component system中,我使用位掩码跟踪和查询每个实体使用哪些组件。

// Thanks to Shafik Yaghmour for the macro fix
#define BIT(x) (static_cast<std::uint64_t>(1) << x)

enum class components : std::uint64_t
{
    foo = BIT( 0),
    bar = BIT( 1),
    // Many more omitted
    baz = BIT(63)
};

// Operator | is used to build a mask of components. Some entities
// have dozens of components.
auto mask = components::foo | components::bar | components::baz

// Do something with the entity if it has the requisite components.
if ( entity->has_components( mask ) ) { ... }

我达到了枚举的64位限制。 C ++枚举(可移植)是否可以大于64位?


更新1:我知道std::bitset,但我无法创建像auto mask = std::bitset<128>{ components::foo, components::baz }这样的掩码,因为std::bitset没有构造函数需要std::initializer_list。如果您的回答告诉我使用std::bitset,请说明此用法或类似内容。


更新2 :我修改了一项功能,以便从std::bitset创建std::initializer_list

enum class components : std::size_t
{
    foo,
    bar,
    baz,
    count
};

template< typename enumeration, std::size_t bit_count = static_cast< std::size_t >( enumeration::count ) >
std::bitset< bit_count > make_bitset(std::initializer_list< enumeration > list)
{
    assert(list.size() <= bit_count);
    std::bitset< bit_count > bs{};
    for (const auto i : list)
    {
        bs.set(static_cast< std::size_t >(i));
    }
    return bs;
}

// Which can create the mask like so:
auto mask = make_bitset< components >( {components::foo, components::baz} );

3 个答案:

答案 0 :(得分:6)

正如其他人所说,存储必须是std::bitset。但是如何将它与枚举接口?使用this answer中概述的运算符重载。枚举器只需要存储位索引号,这会给你带来更多的空间;)。

请注意,该代码必须在移位操作中删除,即:

flag_bits( t bit ) // implicit
    { this->set( static_cast< int >( bit ) ); }

http://ideone.com/CAU0xq

答案 1 :(得分:3)

使用std::bitset


<强>附录:
在我写完上面的答案之后,问题已经更新了,提到了std::bitset和一些关于如何使用它的想法,到目前为止已经有13个版本。

我没能预见使用std::bitset会有任何困难,抱歉。因为它不是引入不必要的复杂性,而是放弃它。例如,而不是现在建议的

#include <bitset>

enum class components : std::size_t
{
    foo,
    bar,
    baz,
    count
};


template< typename enumeration, std::size_t bit_count = static_cast< std::size_t >( enumeration::count ) >
std::bitset< bit_count > make_bitset(std::initializer_list< enumeration > list)
{
    assert(list.size() <= bit_count);
    std::bitset< bit_count > bs{};
    for (const auto i : list)
    {
        bs.set(static_cast< std::size_t >(i));
    }
    return bs;
}

auto main() -> int
{
    auto mask = make_bitset< components >( {components::foo, components::baz} );
}

就是这样做。

#include <bitset>

namespace component {
    using std::bitset;

    enum Enum{ foo, bar, baz };
    enum{ max = baz, count = max + 1 };

    using Set = bitset<count>;

    auto operator|( Set const s, Enum const v )
        -> Set
    { return s | Set().set( v, true ); }
}  // namespace component

auto main() -> int
{
    auto const mask = component::Set() | component::foo | component::baz;
}

或简单地说,没有语法糖,

#include <bitset>

namespace component {
    using std::bitset;

    enum Enum{ foo, bar, baz };
    enum{ max = baz, count = max + 1 };

    using Set = bitset<count>;
}  // namespace component

auto main() -> int
{
    auto const mask = component::Set().set( component::foo ).set( component::baz );
}

答案 2 :(得分:3)

注意,您当前的代码具有未定义的行为,整数文字1在此处具有类型 int

 #define BIT(x) (1 << x)
                 ^

在大多数现代系统上可能都是32 bits因此根据草案C ++标准部分32 bit 移位运算符5.8或更多移位是未定义的行为>其中说:

  

如果右操作数为负数,或者大于或等于提升左操作数的位长度,则行为未定义。

对此的修复方法是使用static_cast1转换为 uint64_t

#define BIT(x) (static_cast<std::uint64_t>(1) << x)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

或者Potatoswatter指出你可以使用ULL后缀,保证为64 bit

没有可移植的方式来使用比 uint64_t 更大的类型,某些编译器(如gcc)提供128 bit整数类型,但同样不能移植。< / p>

std::bitset应该提供您需要的界面。您可以使用set将任何位设置为任意值,并使用test来测试特定位的值。它提供binary or,它应提供此功能:

auto mask = components::foo | components::baz

例如:

#include <bitset>
#include <iostream> 

int main() 
{
    std::bitset<128> foo ;
    std::bitset<128> baz ;

    foo.set(66,true ) ;
    baz.set(80,true ) ;        

    std::cout << foo << std::endl ;
    std::cout << baz << std::endl ;

    std::bitset<128> mask = foo | baz ;

    std::cout << mask << std::endl ;

    return 0;
}