C++ 枚举
概述
C++中,枚举分为两大类,分别是:有作用域枚举,无作用域枚举
他们的区别在于,有作用域枚举将枚举项引入的是自己的作用域内,无作用域枚举则引入的是包含它的内层作用域.
struct C{
enum EN{
E1,E2
};
};
struct C1{
enum struct EN{
E1,E2
};
};
void Foo(){
C::EN e1 = C::E1;
C1::EN e2 = C1::EN::E1; //必须如此才可以访问到枚举项
C1 NM;
C1::EN e3 = NM.EN::E1;
}
实际上还有以下区别:无作用域枚举可以隐式转换到整形(之后整形可以进一步进行类型提升),而有作用域的不行,只能通过显式类型转换来做到获取当前枚举变量的值.
enum Foo { a, b, c };
enum struct Foo1{a,b,c};
class EE{
public:
EE(float se){
}
};
int aa(){
Foo s;
Foo1 ss = Foo1{1}
//C++17 可行
EE sw(s);
//此处,首先隐式类型转换到整形以后类型提升.
//EE ws(ss);
return 0;
}
语法
enum-keyword attr id : base { 枚举项列表 };
emun-keyword attr nested-name-specifier id : base;
nested-name-specifier,即当且仅当出现在模板成员显式特例化时:
template<typename T>
struct T1{
enum EA{a,b,c};
};
template<>
enum T1<int>::EA{e,f,d};
int main(){
T1<char> s;
int a = s.a;
T1<int> ss;
a = ss.e;
}
第一种,attr和:base,还有枚举项类表是可选的,对应的枚举声明有:
无作用域枚举:
enum 名字{枚举项 = 常量表达式, ... };
enum 名字:类型{枚举项 = 常量表达式, ...};
其中第一种是没有指定底层类型的,此时底层类型由枚举项最大值同一个可以完全表示的整形表示(从int开始)
考虑以下例子:
enum T1{a,b,c};
此时若没有初始化器,那么该项的值就是前一项的值加一,若第一个枚举项没有初始化器,那么该项值为0
第二种是有指定底层类型的,例:
enum T1:int{a,b,c};
对于无作用域枚举,其枚举项可以用于任何要求常量表达式的位置,因此以下式子是正确的:
enum T1{a,b,c = a + 1};
有作用域枚举:
enum struct | class 名字{枚举项 = 常量表达式, ... };
enum struct | class 名字:类型{枚举项 = 常量表达式, ...};
当没有底层类型的时候,有作用域枚举默认类型是int
第二种不可见枚举:
不可见枚举在声明之后就被认为是一个完整类型了,因此其必须有显式(隐式)的底层类型
因此,对于无作用域枚举必须要的是底层类型,而有作用域枚举如果没有指定底层类型的话是默认为int,故可以不需要指定底层类型,具体语法如下:
enum 名字:类型;
enum struct|class 名字:类型;
enum struct|class 名字;
两种枚举的初始化和类型转换
初始化:
可以直接以枚举项对枚举变量进行初始化.
在C++17之后,有枚举可以通过直接列表初始化对其进行定义(列表初始化见这里
但是要满足下列条件:
- 初始化列表内只有一个元素
- 枚举有固定的底层类型
- 非窄化转换
类型转换:
在向某个类型转换上面,有作用域和无作用域枚举也是有一定区别的:
- 无作用枚举可以直接隐式类型转换为其底层类型(若指定),未指定的情况下则转换为遵从之前的无作用域底层类型选择的那个类型,并之后按照隐式类型转换的步骤走.
- 有作用域枚举不可以隐式类型转换到其底层类型,但是可以通过显式类型转换做到.
对于其他值向枚举类型转换,以上两者都相同的规定:
- 若底层类型指定,则通过显式类型转换即可,其效果等同于从T1到底层类型的隐式类型转换
- 若未指定,则要求其源值可以小于当前枚举的最大值的位域的最大值,例如枚举项值为1,5,9,则当前转换的源值最大为15(0~4位),若大于15,则行为是未定义的.