【问题标题】:Equivalent of "using namespace X" for scoped enumerations?等效于范围枚举的“使用命名空间 X”?
【发布时间】:2012-03-16 01:25:32
【问题描述】:

我正在使用范围枚举来枚举我正在实现的某个状态机中的状态。例如,我们可以这样说:

enum class CatState
{
    sleeping,
    napping,
    resting
};

在我定义状态转换表的 cpp 文件中,我想使用与 using namespace X 等效的内容,这样我就不需要在所有状态名称前加上 CatState:: 前缀。换句话说,我想使用sleeping 而不是CatState::sleeping。我的转换表有很多列,因此避免使用 CatState:: 前缀会使内容更加紧凑和可读。

那么,有没有办法避免一直输入CatState::?


是的,是的,我已经意识到using namespace 的陷阱。如果有强类型枚举的等价物,我保证只在我的 cpp 实现文件的有限范围内使用它,而不是邪恶的。

【问题讨论】:

标签: c++ enums c++11


【解决方案1】:

那么,有没有办法避免一直输入CatState::?

不是在 C++20 之前。就像没有为静态类成员键入 ClassName:: 的等价物一样。你不能说using typename ClassName 然后进入内部。强类型 enums 也是如此。

C++20 添加了 using enum X 语法,它看起来像这样。

您当然可以不使用enum class 语法,只使用常规enums。但是你会失去强类型。

应该注意,对弱类型枚举使用 ALL_CAPS 的原因之一是为了避免名称冲突。一旦我们有了完整的作用域和强类型,枚举的名称就是唯一标识的,并且不能与其他名称冲突。能够将这些名称带入命名空间范围将重新引入这个问题。因此,您可能希望再次使用 ALL_CAPS 来帮助消除名称的歧义。

【讨论】:

  • +1 谢谢。我把强枚举想象成一个命名空间之类的东西,但现在把它当作一个(有点)类更有意义。
  • 从 C++20 开始,通过论文 p1099r4 将不再是这种情况,谢天谢地。
  • 每个人都有,但我个人不喜欢枚举数的 ALL_CAPS 约定,因为它可能与宏发生冲突。 NULL 特别是在 C++ 中做 JSON 的时候让我很恼火。
【解决方案2】:

所以简短的回答是否定的,但幸运的是,这将在最近完成的 C++20 功能集中发生变化。根据accepted proposal,您将能够执行以下操作:

enum class CatState
{
    sleeping,
    napping,
    resting
};

std::string getPurr(CatState state)
{
    switch (state)
    {
        using enum CatState;
        // our states are accessible without the scope operator from now on

        case sleeping:      return {};      // instead of "case CatState::sleeping:"
        case napping:       return "purr";
        case resting:       return "purrrrrr";
    }
}

【讨论】:

  • 很高兴这是在 C++20 中!这使得作用域枚举更加有用。 PS:将 using 尽可能低,在这种情况下,在 switch 内部(见编辑)
【解决方案3】:

我也很想拥有这种可能性,但我发现这个限制很烦人。通常最好让程序员决定他想使用哪些功能。明确范围或更方便的方式。如果您限制程序员,他要么为了方便而放弃整个功能,要么发明丑陋的变通方法,例如以下基于模板的类型安全枚举。不优化编译会产生一些开销。

template<class _Enum>
class type_safe_enum
{
private:
    _Enum m_EnumValue;
    operator int();
public:
    inline operator _Enum() const { return m_EnumValue; }
    inline void operator =(_Enum x) { m_EnumValue = x; }
};

enum _MY_ENUM
{
    Value1,
    Value2
};

enum _MY_ENUM2
{
    Value3,
    Value4
};

typedef type_safe_enum<_MY_ENUM> MY_ENUM;

void TestMyEnum()
{
    MY_ENUM myEnum;
    int x;

    myEnum = Value1; // ok
    // myEnum = Value3; // compilation error
    // myEnum = 0; // compilation error
    // x = myEnum; // compilation error

}

【讨论】:

  • 您的_Enum 和其他_[A-Z]+ 标识符会导致未定义的行为,请参阅rules about underscores。即All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use.; If the program declares or defines an identifier in a context in which it is reserved&lt;...&gt;, the behavior is undefined..
【解决方案4】:

Nicol 的回答是正确的:该语言旨在让您始终限定作用域枚举器(enum { } 作用域本身除外)。

但是,here is a technique 我想出了在所选类中没有作用域的“作用域”枚举器。从技术上讲,枚举器是无范围的,因此它们仍将隐式转换为int。 (不是你所说的“强类型”。)然而,在成语中,它们是在真正的 enum 名称之后使用范围运算符访问的,所以在语法上没有区别——因此它需要 C++11 .

#define IMPORTABLE_ENUM( TYPENAME, ... ) \
\
struct import_ ## TYPENAME { \
    enum TYPENAME { \
        __VA_ARGS__ \
    }; \
}; \
\
typedef import_ ## TYPENAME :: TYPENAME TYPENAME;

// usage:
IMPORTABLE_ENUM ( duck, huey, dewey, louie )

duck d = duck::dewey; // can't use unscoped enumerators here

struct duck_madness : private import_duck { // but inside a derived class
    duck who_did_it() { return huey; } // qualification is unnecessary
};

【讨论】:

    【解决方案5】:

    您可以考虑使用typedef 来缩短限定名称:

    typedef CatState C;
    

    或者,如果列以可以轻松生成的方式重复,您可以考虑使用宏来生成表中的每一行,这样可以生成非常简洁(并且更易于阅读)的代码。

    【讨论】:

    • 所以你的意思是枚举不存在using namespace 的等价物?如果是这样的话,那么简短的 typedef 对我来说似乎是最好的解决方案。
    • 我不知道,不。您可能可以为每个枚举器使用 using 声明(例如,using CatState::sleeping; 等),但我不能 100% 确定。但是,如果代码高度重复,我强烈建议使用宏。
    • +1 我最终使用了您的 typedef 建议,但 Nicol 直接回答了我的问题。我希望我能接受这两个答案。
    • @James 禁止在作用域枚举器上使用 using 声明。
    • typedef也可以拼写:using C = CatState;
    猜你喜欢
    • 2013-04-13
    • 2014-04-15
    • 2014-12-19
    • 2011-10-28
    • 1970-01-01
    • 1970-01-01
    • 2012-10-18
    • 2016-06-20
    相关资源
    最近更新 更多