枚举值等同于许多其他值

时间:2015-09-25 09:41:02

标签: c++ c++11 enums

我需要使用各种值的枚举,在这种情况下是各种建筑物。其中大多数都是独一无二的,但有一些我希望是等同的。我的意思如下:

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)) {
}

问题是,我有很多碎片,我的理解是让旗帜工作我必须使用两个的力量。这意味着如果我使用uint32我最多可以拥有32件,而且我不想受此限制。我可能只需要20左右,但我不想被卡住。

有什么建议吗?此时我需要使用枚举,所以我不能尝试不同的类型。

4 个答案:

答案 0 :(得分:2)

我建议不要重载==以具有这种意义。 ==通常是可传递的(如果A == B和B == C,那么A == C),如果它不能传递,否则“理智”代码将会中断。

从你的枚举开始:

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

答案 1 :(得分:1)

你正朝着正确的方向前进。你拥有的每种墙类型代表一个位,这太棒了。现在你所要做的就是将它们组合在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;
}

答案 2 :(得分:1)

您可以定义自己的比较运算符,如下所示:

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

请注意,上述的声明(虽然不一定是定义)必须在您希望比较这些内容的任何地方都可见,因此您应该在枚举声明旁边声明它。

答案 3 :(得分:0)

以下是两种略有不同的可能性:

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

第二个版本仍然封装了枚举类型的概念。