枚举类型的命名空间 - 最佳实践

Posted

技术标签:

【中文标题】枚举类型的命名空间 - 最佳实践【英文标题】:namespaces for enum types - best practices 【发布时间】:2010-10-03 17:20:07 【问题描述】:

通常,需要同时使用多个枚举类型。有时,一个人有一个名字冲突。想到了两个解决方案:使用命名空间,或使用“更大”的枚举元素名称。不过,命名空间解决方案有两种可能的实现方式:具有嵌套枚举的虚拟类,或完整的命名空间。

我正在寻找所有三种方法的优缺点。

例子:

// oft seen hand-crafted name *** solution
enum eColors  cRed, cColorBlue, cGreen, cYellow, cColorsEnd ;
enum eFeelings  cAngry, cFeelingBlue, cHappy, cFeelingsEnd ;
void setPenColor( const eColors c ) 
    switch (c) 
        default: assert(false);
        break; case cRed: //...
        break; case cColorBlue: //...
        //...
    
 


// (ab)using a class as a namespace
class Colors  enum e  cRed, cBlue, cGreen, cYellow, cEnd ; ;
class Feelings  enum e  cAngry, cBlue, cHappy, cEnd ; ;
void setPenColor( const Colors::e c ) 
    switch (c) 
        default: assert(false);
        break; case Colors::cRed: //...
        break; case Colors::cBlue: //...
        //...
    
 


 // a real namespace?
 namespace Colors  enum e  cRed, cBlue, cGreen, cYellow, cEnd ; ;
 namespace Feelings  enum e  cAngry, cBlue, cHappy, cEnd ; ;
 void setPenColor( const Colors::e c ) 
    switch (c) 
        default: assert(false);
        break; case Colors::cRed: //...
        break; case Colors::cBlue: //...
        //...
    
  

【问题讨论】:

首先,我会使用 Color::Red、Feeling:Angry 等 好问题,我使用了命名空间方法.... ;) 所有东西上的“c”前缀都会影响可读性。 请注意,你不需要像enum e ...那样命名枚举,枚举可以是匿名的,即enum ...,这在包装在命名空间或类中时更有意义。 如果你有一个未命名的枚举,它的类型是什么?例如:枚举 FOO;空栏(FOO e);但如果我们有 enum void bar2(???); 【参考方案1】:

C++03 原答案:

namespace(相对于class)的好处是您可以在需要时使用using 声明。

使用namespace问题是命名空间可以在代码的其他地方扩展。在大型项目中,您不能保证两个不同的枚举不会都认为它们被称为eFeelings

对于看起来更简单的代码,我使用struct,因为您可能希望内容是公开的。

如果您正在执行这些实践中的任何一个,那么您就处于领先地位,可能不需要进一步审查。

较新的 C++11 建议:

如果您使用的是 C++11 或更高版本,enum class 将在枚举名称内隐式限定枚举值。

使用enum class,您将失去与整数类型的隐式转换和比较,但实际上这可能会帮助您发现模棱两可或有缺陷的代码。

【讨论】:

我同意结构理念。并感谢您的赞美:) +1 我不记得 C++11 的“枚举类”语法。如果没有该功能,枚举是不完整的。 他们是否可以将“使用”用于“枚举类”的隐式范围。例如将添加“使用 Color::e;”编码允许使用“cRed”并知道这应该是 Color::e::cRed?【参考方案2】:

我已经将前面的答案混合成这样的内容:(编辑:这仅对 C++11 之前的版本有用。如果您使用的是 C++11,请使用 enum class

我有一个包含我所有项目枚举的大头文件,因为这些枚举在工作类之间共享,将这些枚举放入工作类本身是没有意义的。

struct 避免了 public: 语法糖,typedef 允许您在其他工作类中实际声明这些枚举的变量。

我认为使用命名空间根本没有帮助。这可能是因为我是一名 C# 程序员,而您必须在引用值时使用枚举类型名称,所以我已经习惯了。

    struct KeySource 
        typedef enum  
            None, 
            Efuse, 
            Bbram
         Type;
    ;

    struct Checksum 
        typedef enum 
            None =0,
            MD5 = 1,
            SHA1 = 2,
            SHA2 = 3
         Type;
    ;

    struct Encryption 
        typedef enum 
            Undetermined,
            None,
            AES
         Type;
    ;

    struct File 
        typedef enum 
            Unknown = 0,
            MCS,
            MEM,
            BIN,
            HEX
         Type;
    ;

...

class Worker 
    File::Type fileType;
    void DoIt() 
       switch(fileType) 
       case File::MCS: ... ;
       case File::MEM: ... ;
       case File::HEX: ... ;
    

【讨论】:

【参考方案3】:

仅供参考,在 C++0x 中,您提到的情况有一种新语法(请参阅C++0x wiki page)

enum class eColors  ... ;
enum class eFeelings  ... ;

【讨论】:

【参考方案4】:

使用类的好处是您可以在它之上构建一个成熟的类。

#include <cassert>

class Color

public:
    typedef enum
    
        Red,
        Blue,
        Green,
        Yellow
     enum_type;

private:
    enum_type _val;

public:
    Color(enum_type val = Blue)
        : _val(val)
    
        assert(val <= Yellow);
    

    operator enum_type() const
    
        return _val;
    
;

void SetPenColor(const Color c)

    switch (c)
    
        case Color::Red:
            // ...
            break;
    

如上例所示,通过使用类,您可以:

    禁止(遗憾的是,不是编译时)C++ 允许从无效值进行强制转换, 为新创建的枚举设置一个(非零)默认值, 添加更多方法,例如返回选项的字符串表示形式。

请注意,您需要声明 operator enum_type() 以便 C++ 知道如何将您的类转换为底层枚举。否则,您将无法将类型传递给 switch 语句。

【讨论】:

此解决方案是否与此处显示的内容有某种关联?:en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Type_Safe_Enum 我正在考虑如何使其成为模板,我不必每次需要使用时都重写此模式它。 @SasQ:看起来很相似,是的。这大概是同一个想法。但是,除非您在其中添加许多“常用”方法,否则我不确定模板是否有益。 1.不是真的。您可以通过 const_expr 或通过 int 的私有构造函数让编译时检查枚举是否有效。【参考方案5】:

我也倾向于将我的枚举封装在类中。

正如 Richard Corden 所指出的,类的好处是它是 c++ 意义上的类型,因此您可以将它与模板一起使用。

我有特殊的 toolbox::Enum 类来满足我的需要,我专门针对提供基本功能的每个模板(主要是:将枚举值映射到 std::string 以便 I/O 更易于阅读)。

我的小模板还具有真正检查允许值的额外好处。编译器在检查值是否真的在枚举中有点松懈:

typedef enum  False: 0, True: 2  boolean;
   // The classic enum you don't want to see around your code ;)

int main(int argc, char* argv[])

  boolean x = static_cast<boolean>(1);
  return (x == False || x == True) ? 0 : 1;
 // main

编译器无法捕捉到这一点一直困扰着我,因为你留下了一个没有意义的枚举值(而且你不会期望)。

同样:

typedef enum  Zero: 0, One: 1, Two: 2  example;

int main(int argc, char* argv[])

  example y = static_cast<example>(3);
  return (y == Zero || y == One || y == Two) ? 0 : 1;
 // main

main 再次返回错误。

问题在于编译器会将枚举拟合为可用的最小表示形式(这里我们需要 2 位),并且适合该表示的所有内容都被视为有效值。

还有一个问题是,有时您宁愿在可能的值上使用循环而不是开关,这样您就不必在每次向枚举添加值时修改所有开关。

总而言之,我的小帮手确实为我的枚举轻松了一些事情(当然,它增加了一些开销),而且这只是因为我将每个枚举嵌套在它自己的结构中:)

【讨论】:

有趣。你介意分享你的 Enum 类的定义吗?【参考方案6】:

使用类或命名空间的区别在于,类不能像命名空间那样重新打开。这样可以避免将来可能会滥用命名空间,但也存在无法添加到枚举集中的问题。

使用类的一个可能的好处是,它们可以用作模板类型参数,而命名空间却不是这样:

class Colors 
public:
  enum TYPE 
    Red,
    Green,
    Blue
  ;
;

template <typename T> void foo (T t) 
  typedef typename T::TYPE EnumType;
  // ...

就个人而言,我不喜欢使用,我更喜欢完全限定的名称,所以我并不认为这是命名空间的优点。但是,这可能不是您在项目中做出的最重要的决定!

【讨论】:

不重新开课也是一个潜在的劣势。颜色列表也不是有限的。 我认为不重新上课是一个潜在的优势。如果我想要更多颜色,我会用更多颜色重新编译这个类。如果我不能做到这一点(比如我没有代码),那么我无论如何都不想碰它。 @MSalters:无法重新开课不仅不是缺点,也是一种安全工具。因为当可以重新打开一个类并向枚举添加一些值时,它可能会破坏已经依赖于该枚举并且只知道旧值集的其他库代码。然后它会愉快地接受这些新值,但在运行时会因不知道如何处理它们而中断。记住开闭原则:类应该对修改关闭,但对扩展打开。通过扩展我的意思不是添加到现有代码,而是用新代码包装它(例如推导)。 所以当你想扩展枚举时,你应该让它成为一个从第一个派生的新类型(如果它在 C++ 中很容易实现的话...... ;/ )。然后它可以被理解这些新值的新代码安全地使用,但只有旧值会被旧代码接受(通过将它们转换下来)。他们不应该接受这些新值中的任何一个类型错误(扩展的类型)。只有他们理解的旧值被接受为正确的(基本)类型(并且意外地也是新类型,因此它也可以被新代码接受)。【参考方案7】:

由于枚举的范围是它们的封闭范围,因此最好将它们包装在 something 中以避免污染全局命名空间并帮助避免名称冲突。我更喜欢命名空间而不是类,因为namespace 感觉就像一个包,而class 感觉就像一个健壮的对象(参见structclass 的辩论)。命名空间的一个可能好处是它可以在以后扩展 - 如果您正在处理无法修改的第三方代码,这很有用。

当然,当我们使用 C++0x 获得枚举类时,这一切都没有实际意义。

【讨论】:

枚举类...需要查一下!【参考方案8】:

我肯定会避免为此使用类;改用命名空间。问题归结为是使用命名空间还是对枚举值使用唯一 ID。就个人而言,我会使用命名空间,以便我的 id 可以更短,并且希望更不言自明。然后应用程序代码可以使用“使用命名空间”指令并使所有内容更具可读性。

从你上面的例子:

using namespace Colors;

void setPenColor( const e c ) 
    switch (c) 
        default: assert(false);
        break; case cRed: //...
        break; case cBlue: //...
        //...
    

【讨论】:

您能否提示一下为什么您更喜欢命名空间而不是类? @xtofl : 你不能写'使用类颜色` @MSalters:你也不能写Colors someColor = Red;,因为命名空间不构成类型。您必须改为写Colors::e someColor = Red;,这很违反直觉。 @SasQ 如果您想在switch 语句中使用Colors::e someColor,即使与struct/class 一起使用,您也不必使用它吗?如果您使用匿名 enum,则交换机将无法评估 struct 抱歉,const e c 对我来说似乎难以阅读 :-) 不要那样做。但是,使用命名空间很好。

以上是关于枚举类型的命名空间 - 最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

最佳实践:C# 扩展方法命名空间和推广扩展方法

XSLT 将命名空间前缀添加到元素最佳实践

智能合约最佳实践 之 Solidity 编码规范

智能合约最佳实践 之 Solidity 编码规范

命名视图的最佳实践 [关闭]

kubernetes资源命名约束和最佳实践