【问题标题】:Enum scoping issues枚举范围问题
【发布时间】:2009-11-15 17:56:17
【问题描述】:

我尽量保持本地化,所以我将枚举放在类范围内,即使它们在两个类之间共享(我把它放在“更好”的类中。)效果很好,但我最近遇到了一个问题,如果我将枚举放在类范围内,就会发生循环依赖。

枚举将成为多个类的构造函数参数,并且它所在的类(以及对它最有意义的类)包括这些类。因此,不能将枚举用作所包含类的构造函数参数,因为它会导致循环依赖。

将这个枚举放在它自己的头文件中会更好吗,如果是这样,我应该把所有的枚举放在头文件中以保持一致吗?这个问题还有其他解决方案吗(合乎逻辑的)?

【问题讨论】:

  • 你尝试过前向声明吗?
  • 前向声明只允许您访问类型的名称。你不能访问接口或实例化它或做任何类似的事情。

标签: c++ scope enums


【解决方案1】:

从 C++11 开始,您可以使用 enum class(或 enum struct - 相同的东西,声明不同),其中枚举值的范围为枚举名称。例如,这是一个有效的 C++11 声明。

enum class token_type {
    open_paren,
    close_paren,
    identifier
};

然而,要访问枚举的值,您必须使用:: 运算符正确地确定它的范围。因此,这是 C++11 中的有效赋值:

token_type type = token_type::close_paren;

但这不是:

token_type type = close_paren;

这解决了命名冲突,意味着您不必使用容器命名空间或结构来阻止值的范围泄漏到不应泄漏的位置。这意味着以下枚举可以存在于与token_type 相同的范围内:

enum class other_enum {
    block,
    thingy,
    identifier
};

现在两个不同结构体中名为identifier的两个值不会相互干扰。

【讨论】:

  • 耶耶耶。 . . . .
【解决方案2】:

我使用迈克尔和罗杰所做的变体:

namespace Color
{
   enum Type
   {
      red,
      green,
      blue
   };
};

void paintHouse(Color::Type color) {...}

main()
{
   paintHouse(Color::red);
}

我发现 Color::TypeColor::ColorCOLOR::Color 更漂亮,更自我记录。如果您觉得Color::Type 过于冗长,可以使用Color::T

我没有为我的枚举值添加前缀(即COLOR_RED),因为枚举周围的命名空间实际上成为了前缀。

我已经停止对作用域常量使用 ALL_CAPS 约定,因为它们与 C 库中的宏(例如 NULL)发生冲突。宏不在定义它们的命名空间中。

【讨论】:

  • 我现在正在考虑采用 Matthieu M 的建议,即使用 struct 而不是命名空间,以便能够将其作为模板参数传递。但是,我想不出为什么我会传递 Color 而不是 Color::Type 作为模板参数。
  • 我现在使用 C++11 范围的枚举,但我希望这个答案对那些受限于 C++98/03 的人仍然有用。
【解决方案3】:

如果枚举被多个类使用,那么我会说它并不真正属于单个类的定义,而是属于这些类所在的命名空间。

除非枚举通过一个类传递给另一个类的构造函数,在这种情况下,单独实例化依赖枚举的类并将其作为参数传递给包含类的构造函数可能更有意义。

【讨论】:

  • 你描述的第二件事是这样,但是你描述的方法行不通。我决定只在具有常用方法等的实用程序标头中提供枚举。
【解决方案4】:

我经常将枚举放在命名空间中,以防止各种枚举值混淆全局命名空间。我认为这就是您通过将它们放在一个班级中尝试做的事情。但是,如果它们不能很好地“适应”一个类,命名空间也可以很好地用于此目的:

namespace FooSettings
{
    enum FooSettings
    {
        foo,
        bar
    };
}
typedef enum FooSettings::FooSettings FooSettingsEnum;


int main()
{
    FooSettingsEnum x = FooSettings::foo;
};

我有一个编辑器 sn-p,它为新枚举构建大纲,只给出它的名称,包括

typedef enum FooSettings::FooSettings FooSettingsEnum;

创建 typedef 的行,因此使用枚举类型声明变量更具可读性。

如果 Stroustrup 有机会,我怀疑 Stroustrup 会将枚举值名称限定在枚举范围内,但 C 兼容性迫使他动手(这只是猜测——也许有一天我会查看 D&E,看看他是否提到任何东西)。

【讨论】:

  • 我个人更喜欢结构体而不是命名空间,因为结构体可以从模板中操作。
  • @Matthieu - 根本没有想到使用结构。我不能说我错过了无法使用模板操作枚举,但我当然也没有看到使用结构来包装枚举范围的缺点。另一个我可以使用的好主意。谢谢。
  • 我尝试将枚举封装在命名空间和结构中,有些事情在结构中效果更好。所以我经常这样做: struct MyEnum { enum Values{a, b}; }; typedef MyEnum::Values MyEnum_t;现在我可以使用MyEnum_t 将枚举称为一种类型,并使用MyEnum::aMyEnum::b 来引用其成员,就好像枚举是一个聚合类型一样。在选择此处的特定命名约定之前,我进行了一段时间的试验,随着时间的推移,我发现上述方法已经尽我所能。
【解决方案5】:

我同意埃米尔的观点。如果您使用 C++98,另一种选择是使用 struct 而不是命名空间,如下

struct Color
{
   enum Type
   {
    red,
    green,
    blue
   };
};

我更喜欢它,因为理想情况下我会使用命名空间来表示包含多个类的模块,而不仅仅是限定枚举范围...

【讨论】:

    【解决方案6】:

    如果枚举是共享的,您应该将它放在任何类之外,但您仍然可以确定枚举的范围。将它放在命名空间中,这样枚举器就不会“泄漏”,从而弄乱项目的命名空间:

    namespace Project { // you already have this, right? :)
      namespace COLOR { // naming styles differ, use what you like
        enum Color {
          red,
          green,
          blue
        };
      }
      using COLOR::Color; // now you can use the type 'normally'
    
      // examples:
      struct A {
        Color c;
        A() : c(COLOR::red) {}
      };
      void f(Color c) {
        using namespace COLOR;
        // inside this function, we no longer have to prefix COLOR::
        if (c == green) {
          go();
        }
        else if (c == red) {
          stop();
        }
      }
    }
    

    【讨论】:

    • 我必须像你一样尝试使用using s=declaration,看看我是否比我一直使用的typedef 更喜欢它。我想我会喜欢它(但可能使用与枚举类型的所有大写字母不同的命名约定)。谢谢!
    • 我使用 CAPS 是因为看起来 Color 会比 COLOR::red 使用更多,或者你会经常使用后者并且可以在函数范围内使用 using 指令。 typedef COLOR::Color Color; 相当于这里的using COLOR::Color;
    【解决方案7】:

    您可以尝试像这样转发声明枚举:

    enum MyEnum;
    

    【讨论】:

    • 枚举不能被向前声明。
    • 即使可以,也不能转发声明嵌套类型。或者更确切地说,您不能在没有嵌套类型定义的情况下前向声明嵌套类型。
    • 作为历史记录,从 C++11 开始,您可以前向声明 enum class(或 enum struct),但这些与旧式 enums 不同。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    • 1970-01-01
    • 1970-01-01
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多