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

Can a C++ enumeration be made bigger than 64 bits?

本文关键字:大于 枚举 是否 C++      更新时间:2023-10-16

在我的实体组件系统中,我使用位掩码跟踪和查询每个实体具有哪些组件。

// 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::initializer_list创建std::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;
}
// Which can create the mask like so:
auto mask = make_bitset< components >( {components::foo, components::baz} );

正如其他人所指出的,存储必须是std::bitset。但是如何将其与枚举接口呢?使用本答案中概述的运算符重载。枚举器只需要存储位索引号,这为您购买了更多的空间;)。

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

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

http://ideone.com/CAU0xq

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

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

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

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

解决此问题的方法是使用 static_cast 将1转换为uint64_t

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

或者正如Potatoswatter指出的那样,您可以使用保证64 bitULL

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

std::bitset 应该提供你需要的接口。可以使用 set 将任何位设置为任何值,并使用 test 来测试特定位的值。它提供二进制文件或应提供此功能的二进制文件:

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;
}

使用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 );
}