我需要使用一个包含各种值的枚举,在本例中是各种建筑部件。大多数都是唯一的,但有一些我希望是等价的。我的意思是:
enum class EPiece: uint8 {
Ceiling,
Table,
Door,
WestWall,
NorthWall,
SouthWall,
EastWall,
Wall,
Floor
};
我想让Wall == WestWall
变成true
,以及Wall == NorthWall
,等等。然而,WestWall == NorthWall
是假的。
为什么我这样做是因为我正在制作一款游戏,其中各种部件都有一个基于它们是什么/它们在哪里的定义。玩家必须按照预先设定的顺序放置各种棋子。玩家首先必须放置NorthWall
棋子。他们将有各种可用的棋子,必须选择一个Wall
棋子,并试图将其放置在NorthWall
棋子上。游戏检查两者是否相等(在本例中为true
),以及当前要放置的棋子是否为NorthWall
。如果他们试图把它放在WestWall
片上,它应该会失败,因为它还没有到那个阶段。
我想通过标记来实现,比如
WestWall = 0x01,
NorthWall = 0x02,
SouthWall = 0x04,
EastWall = 0x08,
Wall = WestWall | NorthWall | SouthWall | EastWall
和检查,执行如下操作:
// SelectedPiece is the Piece the Player selected and is attempting to place
// PlacedOnPiece is the Piece that we are attempting to place on top of
// CurrentPieceToPlace is what Piece we are supposed to place at this stage
if ((CurrentPieceToPlace == PlacedOnPiece) && (SelectedPiece & PlacedOnPiece != 0)) {
}
问题是,我有很多零件,我的理解是,要让这些旗帜起作用,我必须使用2的幂。这意味着如果我使用uint32
,我最多可以有32个棋子,我不想受此限制。我可能只需要20个左右,但我不想被困住。
有什么建议吗?此时我需要使用enum,所以我不能尝试其他类型。
我建议不要重载==
来实现这个含义。==
通常是可传递的(如果A==B和B==C,那么A==C),如果它不能传递,否则"理智"的代码将会崩溃。
从enum开始:
enum class EPiece: uint8 {
Ceiling,
Table,
Door,
WestWall,
NorthWall,
SouthWall,
EastWall,
Wall,
Floor
};
现在定义一个can_be_used_as_a
关系。
bool can_be_used_as_a( EPiece x, EPiece used_as_a_y ) {
if (x==y) return true;
switch(x) {
case Wall: {
switch(used_as_a_y) {
case WestWall:
case EastWall:
case NorthWall:
case EastWall:
return true;
default: break;
}
}
default: break;
}
switch(used_as_a_y) {
case Wall: {
switch(x) {
case WestWall:
case EastWall:
case NorthWall:
case EastWall:
return true;
default: break;
}
}
default: break;
}
return false;
}
现在can_be_used_as_a( WestWall, Wall )
是true
,因为WestWall
可以用作Wall
。同理,Wall
也可以作为WestWall
使用。但是WestWall
不能作为EastWall
使用。
如果您希望语法更简洁一些,可以编写一个命名操作符:
namespace named_operator {
template<class D>struct make_operator{make_operator(){}};
template<class T, char, class O> struct half_apply { T&& lhs; };
template<class Lhs, class Op>
half_apply<Lhs, '*', Op> operator*( Lhs&& lhs, make_operator<Op> ) {
return {std::forward<Lhs>(lhs)};
}
template<class Lhs, class Op, class Rhs>
auto operator*( half_apply<Lhs, '*', Op>&& lhs, Rhs&& rhs )
-> decltype( invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) ) )
{
return invoke( std::forward<Lhs>(lhs.lhs), Op{}, std::forward<Rhs>(rhs) );
}
}
表示12行命名操作符库,如:
struct used_as_a_tag{};
static const named_operator::make_operator<used_as_a_tag> can_use_as_a;
bool invoke( EPiece x, used_as_a_tag, EPiece y ) {
return can_be_used_as_a(x,y);
}
现在我们可以这样做:
if (x *can_use_as_a* y) {
}
,操作符出现在左右操作数之间。但这可能太过分了。
最后,考虑使用enum class
代替enum
。
你的方向是对的。你拥有的每一种墙壁类型都代表一个比特,这非常棒。现在您所要做的就是将它们合并到Wall
中,并在检查中提取它们,因此:
WestWall = 0x01, //0b0001
NorthWall = 0x02, //0b0010
SouthWall = 0x04, //0b0100
EastWall = 0x08, //0b1000
Wall = 0xF //0b1111
现在,要检查枚举中的一个值是否代表另一个值,您应该这样写:
bool isSame(EPiece first, EPiece second)
{
//if they are the same, they are, well... the same.
if(first == second)
return true;
//this only leaves the bits that are present in both values, so
//if the result is different from 0, then second is a part of first, so
//we return true
else if(first & second)
return true;
//if we are here, then first and second are unrelated
return false;
}
您可以定义自己的比较操作符,如下所示:
bool operator==(EPiece lhs, EPiece rhs)
{
if (int(lhs) == int(EPiece::Wall) &&
(int(rhs) == int(EPiece::NorthWall) ||
int(rhs) == int(EPiece::SouthWall))) // lots more cases...
{
return true;
}
return int(lhs) == int(rhs);
}
请注意,上面的声明(虽然不一定是定义)必须在您希望比较这些内容的任何地方可见,因此您应该在enum声明旁边声明它。
这里有两种稍微不同的可能性:
enum {
Flag0 = 1 << 0,
Flag1 = 1 << 1,
Flag2 = 1 << 2,
Flag3 = 1 << 3,
FlagMask = 0x07
}
if (value & FlagMask) // it's got some flags
{ ... }
if (value & Flag3) // Flag3
{ ... }
和
enum {
ItemA0,
ItemABegin = ItemA0,
ItemA1,
ItemA2,
// insert ItemAs here
ItemAEnd,
ItemB0,
ItemBBegin = ItemB0,
ItemB1,
// insert ItemBs here
ItemBEnd,
}
if (ItemABegin <= value && value < ItemAEnd) // it's some ItemA
{ ... }
if (ItemBBegin <= value && value < ItemBEnd) // it's some ItemB
{ ... }
switch (value) { // switch on specific types
case ItemB0: ... break;
case ItemB1: ... break;
}
第二个版本仍然封装了枚举类型的思想。