通常,需要一起使用几种枚举类型。有时,一个人有一个名字冲突。我想到了两个解决方案:使用命名空间,或使用“更大”的枚举元素名称。仍然,命名空间解决方案有两种可能的实现:带有嵌套枚举的虚拟类或完整的命名空间。
我正在寻找这三种方法的优缺点。
示例:
// 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: //...
//...
}
}
答案 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;
}
}
如上例所示,通过使用类,您可以:
请注意,您需要声明operator enum_type()
,以便C ++知道如何将您的类转换为基础枚举。否则,您将无法将类型传递给switch
语句。
答案 6 :(得分:5)
由于枚举的范围是它们的封闭范围,因此最好将它们包装在某些中,以避免污染全局命名空间并帮助避免名称冲突。我更喜欢命名空间,因为namespace
感觉像是一个持有,而class
感觉就像一个健壮的对象(参见struct
与class
辩论。命名空间的一个可能的好处是它可以在以后扩展 - 如果您正在处理无法修改的第三方代码,则非常有用。
当我们使用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位),并且适合此表示的所有内容都被视为有效值。
还有一个问题是,有时你宁愿在可能的值而不是开关上循环,这样你就不必在每次向枚举值添加值时修改所有切换。
总而言之,我的小帮手真的为我的枚举缓解了事情(当然,它增加了一些开销)并且这是唯一可能的,因为我将每个枚举嵌套在它自己的结构中:)