%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
![[02-开发笔记/01-cpp/类型相关/cpp-枚举类型 Enumeration#^rin6gm]]
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
❓<font color="#c0504d">枚举与枚举类有什么区别?</font>
❓<font color="#c0504d"> 枚举值是如何使用的?</font>
%%
<br><br>
# 枚举类型 Enumeration
**枚举类型**(`Enumeration`) 是一种**用户自定义类型**,包含一组 "**==具名常量==**"(称为**枚举值**)[^4]。
枚举类型中的每个 **==枚举值==**(enumerator)均为 "**整型字面值常量**",其本身就是一条 "**==常量表达式==**"。
C++中的**枚举类型**有两种:
- **未限定作用域的枚举类型**(unscoped enumeration):由 `enum` 关键字声明
- **==限定作用域==的枚举类型**(scoped enumeration):由 **==`enum class`==** 关键字声明(C++11 起),也称 "**==枚举类==**"
```cpp
// 枚举类型名称为Color, 枚举值有: RED, BLUE, GREEN.
// 未限定作用域的枚举类型
enum Color { RED, BLUE, GREEN }
Color c = RED;
// 限定作用域的枚举类型
enum class Color { RED, BLUE, GREEN }
Color c = Color::RED; // 引用枚举值必须声明类作用域
```
## 两种枚举类型的区别
> ❓「<font color="#c0504d">未限定作用域的枚举类型」与 「限定作用域的枚举类型」有什么区别?</font> ^rin6gm
区别如下:
| | 访问枚举值 | 枚举值的==隐式类型转换== | 枚举值的==默认类型== | 前置声明中是否必须 "==明确==枚举值类型" | 是否可匿名 |
| -------------------------------- | ------------------------------------------ | -------------- | ------------ | ----------------------- | ----- |
| **未限定作用域**的`enum` | 直接访问 | ✔️ 支持隐式转为整型 | 无默认类型 | ✔️ 必须明确指定 | ✔️ |
| **限定作用域**的 `enum class`<br>(枚举类) | 需要 "**作用域运算符** `::`"<br>(枚举值属于**枚举类**的成员) | ❌ 要求必须显式转换 | `int` | 可省略,省略时**默认 `int`** | ❌ |
> [!note] 参见 [^1] [^2]
>
>
>
> ![[_attachment/02-开发笔记/01-cpp/类型相关/cpp-枚举类型 Enumeration.assets/IMG-cpp-枚举类型 Enumeration-A0CE8587E83E4FACBB1E72F6E79942B7.png|671]]
```cpp
// 比较两种枚举类型
enum Fruit { APPLE, ORANGE, BANANA };
enum class Color { RED, BLACK, GREEN };
void comparison_exam() {
// 1.未限定作用域枚举类型
// 1) 可直接访问枚举值
// 2) 其枚举值支持隐式类型转换
Fruit ff = APPLE;
int f1 = BANANA; // 隐式类型转换
int f2 = ff;
cout << APPLE << endl;
cout << ff << endl;
// 2.限定作用域枚举类型
// 1) 必须通过`枚举类型名::枚举值`访问枚举值
// 2) 其枚举值不支持隐式类型转换, 必须显式转换(如下)
Color cc = Color::RED;
int c1 = (int) Color::BLACK; // 必须显式类型转换
int c2 = (int) cc; // 必须显式类型转换
cout << (int) cc << endl;
cout << (int) Color::RED << endl;
}
```
<br>
## 枚举类型的前置声明
> 参见 [^2](Item10)
- 「**无限域的枚举类型**」,仅当 "**==指定了枚举值的底层类型==**" 时才能进行前置声明;
- 「**==限定作用域==的枚举类型**」总是可以进行前置声明。(因为其枚举值的**底层类型默认为 `int`**)
> [!example]
>
> ```cpp
> // 前置声明
> // 未限定作用域的枚举类型, 必须在前置声明中指定其枚举值的底层类型, 同时定义处也必须指明.
> enum Dog : long long;
> enum Dog : long long { Dog1, Dog2, Dog3 };
>
> // 限定作用域的枚举类型, 可省略, 因为其默认类型为`int`.
> enum class cat; // 默认int
> enum class cat { cat1, cat2, cat3 }; // 默认int
>
> enum class Number : unsigned long long; // 如果声明中指定了底层类型, 则必须在定义处同样给出.
> enum class Number : unsigned long long { NUM1, NUM2, NUM3 };
> ```
>
<br>
## 匿名枚举类型
仅 "**==未限定作用域==的枚举类型**" 可以**省略类型名称**,称为 "**匿名枚举类型**"。
当 "**仅需要定义一组整型常量**",且**不需要在其他地方引用该枚举类型**(因而不需要其名称)时,可使用**匿名枚举类型**。
> [!example]
>
> ```cpp
> // 匿名枚举类型(仅未限定作用域的枚举类型允许匿名)
> enum { AA, BB, CC }; // 匿名枚举类型, 其枚举值可直接访问
> void anonymous_enum_exam() {
> cout << AA << endl;
> cout << BB << endl;
> }
> ```
>
>
<br><br>
## 枚举对象
枚举是一种 "类型",因此对于 "**==具名==枚举类型**",可以**定义并初始化==该类型的变量==**——**枚举对象**。
**枚举对象** 只能其**所属枚举类型**的 "**==枚举值==**" 进行**初始化、赋值**。
> [!caution] 不存在整型到 "枚举类型" 的隐式转换! => 不能用整型初始化或赋值给 "枚举对象"
> [!example]
>
> ```cpp
> enum Color { RED, BLUE, GREEN };
> Color cc = Color::RED;
> cc = Color::BLUE;
>
> // Color cc = 0; // error
> // cc = 2; // error
> ```
>
>
<br><br>
# 枚举值 Enumerator
枚举类型中的每个 **==枚举值==**(enumerator),其类型为 "**整型字面值常量**"。
每个枚举值本身都是 "**常量表达式**",可用**任何常量表达式==初始化其值==**。
### 枚举值的值
枚举类型中,**各个枚举值对应的值==默认从 0 开始,依次加 1==**。
可以为每个枚举值**显式指定初始值**,未指定值的成员其值等于 "**==前一项枚举值的值 + 1==**" 。
```cpp
enum Color { RED, BLUE = 4, GREEN}; // RED==0, BLUE==4, GREEN==5
```
### 枚举值的底层类型
- 未限定作用域的枚举类型: 其**枚举值无默认的底层类型** ,由编译器决定。
- **限定作用域**的枚举类型: 其**枚举值的类型==默认为 `int`==**;
可在枚举类型名称后通过 `:` 指定每个**枚举值的默认类型**,如下所示:
```cpp
enum Dog : int { DG1, DG2, DG3 };
enum class intTypes : unsigned long long { // 指定枚举值类型为ULL
charType = 255,
shortType = 65535,
intType = 65535,
longType = 4294967295UL,
llongType = 18446744073709551615ULL
};
```
<br>
# 枚举值使用示例
> 参见 [^1] [^3]
```cpp
// 示例1: 可定义枚举类型的constexpr变量:
enum class intTypes {
charType = 8, shortType = 16, intType = 32,
longType = 32, long_longType = 63
};
constexpr intTypes charbits = intTypes::charType;
// 示例2: 可将枚举值作为switch语句中的case标签
enum class LogLevel { DEBUG = 0, INFO, WARN, ERROR, FATAL };
void func (LogLevel key) {
switch(key) {
case LogLevel::DEBUG : {
...
} break;
case LogLevel::INFO : {
...
} break;
...
}
}
// 示例3: 可将枚举值用于定义静态数组大小.
struct MyClass {
enum {
INT_ARRAY_SIZE = 32,
BUFF_SIZE = 1024
};
int array[INT_ARRAY_SIZE];
char buffer[BUFF_SIZE];
};
// 示例4: 枚举值用作非类型模版参数的实参
template <int V>
void addN(int& val) {
val += V;
}
enum { VAL1 = 5, VAL2 = 15 };
int main() {
int arr[5] { 1, 2, 3, 4, 5 };
for_each(begin(arr), end(arr), addN<VAL1>);
for_each(begin(arr), end(arr), addN<VAL2>);
return 0;
}
// 示例5: 可在类中初始化 "枚举类型" 的静态数据成员
enum class MyEnum { KT = 5, DG = 6};
class MyClass {
public:
MyClass() = default;
private:
MyEnum mem = MyEnum::DG;
};
```
<br><br>
# 参考资料
# Footnotes
[^1]: 《C++程序设计语言》P188-189
[^2]: 《Effective Modern C++》Item 10
[^3]: 《C++ Primer》P737
[^4]: 《C++ Primer》P736-P739