【问题标题】: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
    相关资源
    最近更新 更多