C++枚举标志用法

C++ enum flag usage

本文关键字:用法 标志 枚举 C++      更新时间:2023-10-16

我已经用谷歌搜索了一下,但似乎没有立即可用的明确答案。我试图弄清楚 C++ 中枚举标志的正确用法。我正在开发一个基于 2D 平铺的平台游戏引擎,我想为每个平铺提供标志,以确定哪些边缘可以碰撞。

在一个文件中,我有:

enum CollidableEdges
{
    TopEdge = 1,
    RightEdge = 2,
    BottomEdge = 4,
    LeftEdge = 8
};

如何在对象中实际调用这些标志,并在以后的逻辑中使用它们?在我在网上看到的示例中,在操作标志时,除了初始列表之外,它似乎没有在任何地方声明枚举。有人可以澄清到底发生了什么以及如何使用它吗?例如,在创建具有如下定义的新 Tile 对象时:

class Tile: public Entity
{
    public:
        Tile(std::string obstacleTexture, int, int);
}

是一个相当新的程序员,所以我确定我正在使用不好的做法或遗漏了一些明显的东西。不要害羞地告诉我。

有人可以澄清到底发生了什么以及如何使用它吗?

初始化特定位

uint8_t flags = uint8_t(TopEdge) | uint8_t(LeftEdge);

设置特定位

flags |= uint8_t(BottomEdge);

清除特定位

flags &= ~(uint8_t(TopEdge) | uint8_t(LeftEdge));

测试特定位

if(flags & uint8_t(BottomEdge) > 0) { // Flag is set (BottomEdge)
}

您需要强制转换它的原因是,如果您有类似 TopEdge | LeftEdge 的表达式,编译器将不接受原始enum类型作为结果。

为了摆脱强制转换,您还可以定义重载operator|()operator&()等:

CollidableEdges operator|(CollidableEdges  left, CollidableEdges right) {
    return static_cast<CollidableEdges>(static_cast<unsigned>(left) | 
                                        static_cast<unsigned>(right));
}
// etc.

允许简单地编写

CollidableEdges edges = TopEdge | LeftEdge;

注意:
尽管上述语句可以正常工作,但恕我直言,它们对预期语义的可读性很差。我个人更喜欢以下解决方案:

 enum class CollidableEdgeBitPos : size_t {
    TopEdge ,
    RightEdge ,
    BottomEdge ,
    LeftEdge 
 };
 typedef std::bitset<4> CollidableEdges;
 CollidableEdges edges;
 edges[CollidableEdgeBitPos::RightEdge] = true;
 edges[CollidableEdgeBitPos::BottomEdge] = false;

CollidableEdges是一种类型,你可以在任何可以使用类型的地方使用它(例如,它可以是方法中参数之一的类型。

枚举的

标签是在枚举的包含范围内有效的标识符。例如,如果在class XX中定义枚举,则可以使用 XX::TopEdge 访问标签

您可以转换为/从 int 转换

,但最好检查(例如开关/大小写)而不是从 int 盲转换。

强制转换为 int 将生成与标签关联的值。这是静态常量整数常量的替代方法。例如,在您的示例中,每个标签都关联一个逐位不相交值,您可以使用按位或(例如 TopEdge|LefEdge)[但只是因为它们是不相交的整数值)。

给定数值,看起来你的枚举正在用于定义位掩码。 在这种情况下,通常的程序将是为他们使相关运营商超载,以便您可以适当地使用它们:

inline CollidableEdges
operator|( CollidableEdges lhs, CollidableEdges rhs )
{
    return static_cast<CollidableEdges>(
        static_cast<unsigned>( lhs) | static_cast<unsigned>( rhs ) );
}

等。 (你需要||=&&=~。 这些运算符通常应与枚举位于同一标头中定义。 然后,使用它们的对象也将采用作为参数的CollidableEdge

Tile::Tile( std::string const& texture, CollidableEdge edges )...

等。

传统上,枚举常量的名称是在与枚举本身相同的范围内定义;在 C++11 中,您还有可能将它们限制为枚举,通过声明enum class CollidableEdge,而不是 enum CollidableEdge ;在这种情况下,您必须指定 CollidableEdge::TopEdge | CollidableEdge::LeftEdge ,而是不仅仅是TopEdge | LeftEdge. (另一方面,这意味着它们不会与您可能想要的任何其他符号发生冲突定义。

最后,鉴于|&的"错误"先例,我经常发现将运算符+-*定义为好吧,a + b相当于a | b(您正在向集合),a - b相当于a & ~ b(您正在减去集合中的值)和a * ba & b相同。