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

时间:2009-01-27 09:17:20

标签: c++ enums scope nested

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

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

示例:

// oft seen hand-crafted name clash 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: //...
        //...
    }
  }

8 个答案:

答案 0 :(得分:65)

原创C ++ 03回答:

来自namespace(超过class)的权益是您可以在需要时使用using声明。

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

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

如果您正在进行任何这些练习,那么您就处于领先地位,可能不需要进一步仔细检查。

较新,C ++ 11建议:

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

使用enum class,您将失去隐式转换和与整数类型的比较,但在实践中,这可能会帮助您发现模糊或错误的代码。

答案 1 :(得分:20)

FYI在C ++ 0x中,对于你提到的案例有一个新的语法(见C++0x wiki page

enum class eColors { ... };
enum class eFeelings { ... };

答案 2 :(得分:11)

我将前面的答案与以下内容混合:(编辑:这仅适用于预C ++ 11。如果您使用的是C ++ 11,请使用enum class

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

struct避免使用public:syntactic sugar,而typedef允许您在其他worker类中实际声明这些枚举的变量。

我认为使用命名空间根本不会有帮助。也许这是因为我是一名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 :(得分:9)

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

从上面的示例中:

using namespace Colors;

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

答案 4 :(得分:7)

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

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

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

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

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

答案 5 :(得分:7)

使用类的优点是你可以在它上面构建一个完整的类。

#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;
    }
}

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

  1. 禁止(遗憾的是,不是编译时)C ++允许从无效值转换
  2. 为新创建的枚举设置(非零)默认值
  3. 添加更多方法,例如返回选择的字符串表示。
  4. 请注意,您需要声明operator enum_type(),以便C ++知道如何将您的类转换为基础枚举。否则,您将无法将类型传递给switch语句。

答案 6 :(得分:5)

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

当我们使用C ++ 0x获取枚举类时,这一切都没有用。

答案 7 :(得分:3)

我也倾向于将我的枚举包装在课堂上。

正如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将返回错误。

问题是编译器会将enum放在可用的最小表示中(这里我们需要2位),并且适合此表示的所有内容都被视为有效值。

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

总而言之,我的小帮手真的为我的枚举缓解了事情(当然,它增加了一些开销)并且这是唯一可能的,因为我将每个枚举嵌套在它自己的结构中:)