自C++11起,引入了新的枚举,至此,C++支持两种枚举,如果将新引入的枚举成为scoped enumerations的话,那么之前的则成为unscoped enumerations。
Unscoped enumerations称为无范围枚举,又称为标准枚举(当然是相对新引入的枚举来说),形如:
enum Colors{
Res,
Green,
Blue
};
这个在C里面也支持,所以在此就不在做详细的使用说明,为了引入本文的主题,例子如下:
namespace MyNamespace {
enum Colors {
Red,
Green,
Blue
};
}
int main() {
MyNamespace::Colors color = MyNamespace::Red;
int enumValue = MyNamespace::Colors::Red;
enumValue = MyNamespace::Red + MyNamespace::Blue;
return 0;
}
如果此时,由于项目的需要,引入了第二个枚举OtherColors
,比较特殊的是OtherColors中有一个与枚举Colors中相同的枚举值Blue,如下:
namespace MyNamespace {
enum Colors {
Red,
Green,
Blue
};
enum OtherColors {
Yellow,
Blue
};
}
int main() {
MyNamespace::Colors color = MyNamespace::Red;
int enumValue = MyNamespace::Colors::Red;
enumValue = MyNamespace::Red + MyNamespace::Blue;
return 0;
}
此时编译器会报如下错误:
<enum.cc>:10:5: error: 'Blue' conflicts with a previous declaration
31 | Blue
| ^~~~
<enum.cc>:5:5: note: previous declaration 'MyNamespace::Colors MyNamespace::Blue'
26 | Blue
而这也就是C++11之前的枚举称为Unscoped enumerations的原因,即枚举的值存在于其声明的任何范围
内。
当然了,我们可以通过其他方式来解决,一种比较常见的方式是通过值+枚举名来实现:
namespace MyNamespace {
enum Colors {
ColorsRed,
ColorsGreen,
ColorsBlue
};
enum OtherColors {
OtherColorsYellow,
OtherColorsBlue,
};
}
显然,这种方式其就是相当于换了枚举值的名字。这种实现方式可以解决前面编译器的报错,但是会降低代码的可读性,因此往往采用另外一种实现方式:
{
enum Enum
{
Red,
Green,
Blue
};
}
namespace OtherColors
{
enum Enum
{
Yellow,
Blue,
};
}
int main() {
Colors::Enum color = Colors::Red;
return 0;
}
这种实现方式,在项目中很常见,代码也很干净,一目了然。尤其是在命名空间内,将枚举名称命名为Enum,那么无论是在声明一个变量、函数参数、或者函数返回值的时候,通过前缀Enum一眼就能看出来是个枚举。并且在访问枚举的成员时,可以通过其命名空间来进行访问(Colors::Red),这种解决冲突的方式清楚简洁,且不会对编译时间运行期造成影响。
PS:在后文中,将此类枚举成为标准枚举。
Scoped enumerations自C++11引入,使用形如enum class
来进行声明,又称为强枚举。与前面所讲的标准枚举相比,强枚举是有作用域的,如果在同一个命名空间内,使用标准枚举可能会引发冲突,而如果使用强枚举,则不会,因为其引入了范围的概念,仍然是前面的例子,引入强枚举后:
namespace MyNamespace {
enum class Colors {
Red,
Green,
Blue
};
enum class OtherColors {
Yellow,
Blue
};
}
int main() {
MyNamespace::Colors color = MyNamespace::Colors::Red;
return 0;
}
不过,需要注意的是,强枚举不能像标准枚举一样进行类型转换,即:
int color = MyNamespace::Colors::Red;
会报错如下:
error: cannot convert 'MyNamespace::Colors' to 'int' in initialization
如果非要进行类型转换,则可以考虑强制类型转换方式:
int coloe = static_cast<int>(MyNamespace::Colors::Red);
相信很多人跟我一样,想知道强枚举的实现方式,此次借助cppinsights
来分析下,仍然是上面的代码:
namespace MyNamespace
{
enum class Colors : int
{
Red,
Green,
Blue
};
}
看了上面代码的第一眼,就好奇enum class Colors : int,莫非强枚举继承于int?针对这个疑问,查了相关资料,发现上面的**:int并不是继承于int,而是指定了枚举的基础类型int**,下面代码摘自于so:
#include <iostream>
enum class tiny : bool {
one, two
};
enum class bigger : long {
some, lots
};
int main(int argc, char *argv[])
{
std::cout << sizeof(tiny::one) << '\n'; //prints 1
std::cout << sizeof(bigger::some) << '\n'; //prints 8
}
以上~