【问题标题】:How to implement scoped enum in C++98 and can be used like enum class in C++11?如何在 C++98 中实现作用域枚举,并且可以像 C++11 中的枚举类一样使用?
【发布时间】:2020-07-30 19:57:27
【问题描述】:

在 C++11 中,我们有 enum 的作用域,我们可以如下使用它。

#include <iostream>
enum class Color
{
    RED,
    BLUE,
};
int main()
{
    Color color = Color::RED;
    if (color == Color::RED)
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

我已经在我的项目中到处使用范围枚举。

现在我必须迁移到 C++98,所以不能再使用作用域枚举。

如何在 C++98 中实现一个作用域枚举并像 C++11 中的枚举一样使用?

如果实现技术复杂,我们可以将其提取到模板中吗?

Follow link已经讲了一些技术,但不像C++11那么简单。

How to use enums in C++

例如:

namespace Color
{
enum MyColor
{
    RED,
    BLUE,
};
}

感谢您的宝贵时间。

【问题讨论】:

标签: c++ c++11 c++98


【解决方案1】:

在 C++11 之前模拟作用域枚举的一种常用方法是在类声明中声明非作用域枚举:

#include <iostream>

struct Color {
   enum MyColor {
      kRed,
      kBlue
   };
};

int main() {
    const Color::MyColor color = Color::kRed;
    if (color == Color::kRed)
    {
        std::cout << "red" << std::endl;
    }
}

或者,作为一个小的变化,为了一些简洁,可能会造成一些混淆(即,在使用站点上:“MyColorColor 如何关联?”):

#include <iostream>

struct Color {
   enum MyColorImpl {
      kRed,
      kBlue
   };
};

typedef Color::MyColorImpl MyColor;

int main() {
    const MyColor color = Color::kRed;
    if (color == Color::kRed)
    {
        std::cout << "red" << std::endl;
    }
}

当然,您也可以使用问题中显示的命名空间范围方法,但它会带来潜在问题,即命名空间可能会在代码库的其他地方扩展,这反过来可能导致意外(开发人员期望)行为;您的模拟范围枚举可以例如开始表现为几个不同的非作用域枚举的串联。

#include <iostream>

namespace color {

enum MyColor {
   kRed,
   kBlue
};

}

namespace color {

enum CarColor {
   kAbsolutelyNotRed
};

}

int main() {
    const color::MyColor color = color::kRed;
    // At best, a -Wenum-compare warning.
    if (color == color::kAbsolutelyNotRed)
    {
        // At worst, a critical logical fault.
        std::cout << "absolutely not red (ups, actually red)" << std::endl;
    }
}

【讨论】:

  • 它有效,但是使用 Color::MyColor 看起来很长,所以我想知道有什么技术可以摆脱它。
  • @XuHui 您可以,可能出于一些混淆,将非作用域的enum 重命名为MyColorImpl,并为枚举类型公开一个别名:typedef Color::MyColorImpl MyColor;。这将允许您在声明枚举变量时使用MyColor,并在访问其枚举器时使用Color::kRed。关于结构范围与命名空间范围,我会说这归结为偏好。我更喜欢在一个类中定义它的强类型。
  • @XuHui 我扩展了一个示例,说明命名空间方法如何导致一些混乱(以防来自良好编译器的有用警告被忽略)。
  • @XuHui 是的,但是对于基于类的枚举,两者之间的作用域语义不同:在语义上会提示您将CarColor::kAColorMyColor 进行比较不是一个好主意;他们有不同的范围。但是,对于命名空间的情况,color 下的两个范围,这意味着您看不到从使用站点到枚举 kColorcolor::kColor 表达式中属于什么。不过,这只是准系统语义,为了真正的类型安全,您可以实现特定的类来模拟枚举(有很多现有的 biolerplate 为此目的)。
  • @XuHui 抱歉,discrete 是错误的术语,我现在已将其编辑为 specific。基本上是一个有点像枚举的类,但具有强类型。不过,这通常带有一些样板文件,我个人只是求助于 C++11 之前的类中的范围枚举(结合 -Wall, -Werror 以及一个好的静态分析工具)。参见例如以this article 为例。
【解决方案2】:

由于drfi已经给出了详细的解释,我还是在这里给出另一个角度来解决问题。

我们可以使用类来实现枚举,并且支持严格的类型检查。

代码修改自书籍Effective C++ 3rd: Item 18中的class Month示例

#include <iostream>
class Color
{
public:
    static Color RED()
    {
        return Color(0);
    }
    static Color BLUE()
    {
        return Color(1);
    }
    bool operator==(const Color &rhs) const
    {
        return this->value == rhs.value;
    }
    bool operator!=(const Color &rhs) const
    {
        return !(*this == rhs);
    }

private:
    explicit Color(int value_) : value(value_) {}
    int value;
};

int main()
{
    Color color = Color::RED();
    if (color == Color::RED())
    {
        std::cout << "red" << std::endl;
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-09
    • 1970-01-01
    • 1970-01-01
    • 2019-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多