【问题标题】:Why put an enum in a struct and then use a typedef name?为什么将枚举放入结构中,然后使用 typedef 名称?
【发布时间】:2018-12-20 20:58:37
【问题描述】:

我发现以下模式在我们公司的代码中相当常用。

struct Foo
{
    enum FType
    {
        TypeA,
        TypeB,
        Type_MAX
    };
};
typedef Foo::FType FooType;
[...]
FooType m_type;

我的问题是,这样做有什么好处? (或者,这避免了什么问题?)为了清楚起见,我想知道他们为什么不...

enum FooType
{
    TypeA,
    TypeB,
    Type_MAX
};
[...]
FooType m_type;

我不能问原来的程序员,因为他们被重新分配了,原来我们公司指定的主题专家实际上是我。

如果有帮助,此代码已在不同时间使用多个版本的 MSVC、gcc 和 clang 编译用于不同的目标平台...所有 C++11 之前的版本。

谁能明白为什么要这样做? 如果答案被证明是微不足道的,请提前道歉。

编辑添加:这是在内部类中使用的。 (当枚举是全局的时,我们的样式指南要求条目以公共前缀开头,以便将它们与其他符号区分开来。)

【问题讨论】:

  • “原来我们公司指定的主题专家,其实就是我”呵呵:)
  • @LightnessRacesinOrbit 我知道那种感觉。有人说你只是一个可能知道的人,在你听完问题后会有一种蜷缩的冲动。或者用头攻击桌子,后悔手头没有锋利的东西。
  • 我总是背诵简单的概念“类名也是一个命名空间”。它解决了大部分的理解问题。
  • @Swift-FridayPie:另见:“专家 - 草图”

标签: c++ enums c++03


【解决方案1】:

普通旧枚举的感知问题是枚举器成为定义枚举的范围内的名称。因此,您可以说,例如,

FooType m_type;
m_type = TypeA;

如果你还定义了一个类名TypeA,你就会有冲突。将枚举放在类中意味着您必须使用范围限定符来获取名称,这将消除冲突。这也意味着你必须写

FooType m_type;
m_type = FooType::TypeA;

因为以前的版本无效。

这个问题的新解决方案是作用域枚举:

enum class FooType {
    TypeA,
    TypeB,
    Type_MAX
};

现在你可以说

FooType m_type;
m_type = FooType::TypeA;

但不是

m_type = TypeA;

正如@Jarod42 指出的那样,这里的一个区别是,由普通枚举定义的枚举器可以隐式转换为int,而来自作用域枚举的枚举器则不能。所以有了类里面的定义,

int i = FooType::TypeA;

是有效的,i 的值为 0。对于作用域枚举,它是无效的。

在这两种情况下,强制转换都可以进行:

int i = static_cast<int>(FooType::TypeA);

【讨论】:

  • 请注意,enum class 不等于类中的enum:没有与int 的隐式转换。
  • @Jarod42 -- 已添加。谢谢。
  • 直到 c++11 编译器无法识别枚举是类范围的一部分。有些人可能会发出有关使用类名的警告。
  • @Swift-FridayPie -- 你是说 C++ 语言定义需要在枚举中定义的枚举器,即在类中定义的枚举器被注入全局范围?我不认为这是规则。你有这个权限吗?还是您在谈论某些特定的编译器?
  • 感谢这个答案(以及那些在下面写了相同/类似答案的人!)回想起来,这似乎很明显。话虽如此....该技术通常在“枚举”在类中时使用,在这种情况下,污染范围应该不是问题。我想我要把它归结为一个太急于开处方的家居风格指南。再次感谢大家。
【解决方案2】:

这样做很可能是为了防止enum 成员污染全局范围。当你有

enum FooType
{
    TypeA,
    TypeB,
    Type_MAX
};

TypeATypeBType_MAX 成为全局范围内的名称。这可能会导致与其他 enums 或其他已在使用的名称发生冲突。通过将enum 放在struct 中,您可以将名称限制为结构的范围。另一种实现方式是使用namespace

C++11 提供 enum classenum 成员的范围保持在 enum 本身,因此如果您可以处理 enum class 施加的更严格的控制,则不再需要这样做。

【讨论】:

  • 使用namespace 并不理想,因为命名空间可以在其他任何地方扩展,而且名称通常使用不合格。
【解决方案3】:

编写此代码时,C++ 中可能没有 enum class,因此这是避免外部命名空间污染的唯一解决方案。

Struct 在这里更像是一个命名空间。仍然作者可能希望枚举本身的名称位于外部命名空间中,这是由 typedef 完成的。

【讨论】:

    【解决方案4】:

    在较早的 C++ 版本中,枚举名称被添加到周围的命名空间中。

    在您的第二个示例中,我可以这样做:

    m_type = TypeA;
    

    在原来的例子中你需要做的:

    m_type = FooType::TypeA;
    

    如果 TypeA 在您的应用程序中相当常见,我可以理解您为什么不想污染它周围的命名空间。

    【讨论】:

      【解决方案5】:

      在 C++11 之前的版本中,您应该始终将 enum 放在 struct(或 class)中。其原因是它可以防止对全局 namespace 的污染,并且在使用结构成员时强制使用限定名称。即Foo::TypeA。为什么?因为否则其他人可能会决定在代码的其他地方创建一个常量或另一个名为 TypeAenum 成员,并且会出现名称冲突。

      typedef 可能只是为了方便不用每次都输入完全限定的enum 类型名称。

      这仅适用于 C++11 之前的版本。 C++11 有enum class,它声明了一个作用域枚举。你提到这段代码是在那之前写的。

      【讨论】:

        【解决方案6】:

        我的问题是,这样做有什么好处? (或者,这样可以避免什么问题?)

        它将单个枚举标识符限制在类的命名空间中:

        int TypeA;
        
        struct Foo
        {
            enum FType
            {
                TypeA, // OK, Foo::TypeA does not conflict with ::TypeA
                TypeB,
                Type_MAX,
            };
        };
        
        enum FooType
        {
            TypeA, // not OK, conflicts with the existing ::TypeA declaration
            TypeB,
            Type_MAX,
        };
        

        程序员可以改用命名空间,这样可能更清楚。

        【讨论】:

          猜你喜欢
          • 2011-07-06
          • 2023-03-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-08-13
          • 1970-01-01
          • 1970-01-01
          • 2013-07-17
          相关资源
          最近更新 更多