C++98 风格的枚举,现在称作为 unscoped enum 。scoped enum 的枚举元素只在 enum 内可见, 元素只能强制转换成其他类型。scoped enum 和 unscoped enum 都可以指定潜在类型。 scoped enum 默认是 int ,unscoped enum 没有默认类型。scoped enum 总是能前置声明,unscoped enum 只有指定了潜在类型, 才可以前置声明。
枚举
枚举(enumeration)是独立的类型,它的值被限制在一个取值范围内,它可以包含数个明确命名的常量(“枚举项(enumerator)”)。各常量的值是某个整型类型(称为该枚举的底层类型(underlying type))的值。
有两种截然不同的枚举:unscoped enum (以 枚举关键词 enum 声明)和scoped enum (以 枚举关键词 enum class 或 enum struct 声明)。
unscoped enum
简要说明
enum 名字(可选) { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , … }
enum Color { red, green, blue };
enum 名字(可选) : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , … } (C++11 起)
enum Color:int { red, green, blue };
enum 名字 : 类型 ; (C++11 起) 前置声明
enum Color:int;
知识点位
多个enum名称或元素可以有相同的值
下面是两个具有相似值的enum元素的示例。
#include <iostream>
using namespace std;
enum Cars{Jeep = 1, BMW = 0, Mercedes_Benz = 0};
int main(){
cout<<"Jeep= "<<Jeep<<" BMW= "<<BMW<<" Benz= "<<Mercedes_Benz;
return 0;
}
默认从0开始
如果不给enum元素赋自定义值,编译器会给它们赋从0开始的默认值。
#include <iostream>
using namespace std;
enum Months{January, February, March, April, May, June, July, August, September, October, November, December};
int main(){
enum Months m = May;
cout<< "以月份为单位的五月的值为 "<< m;
return 0;
}
未赋值的前项+1
可以以任何顺序为enum中的任何元素提供值。所有未赋值的元素将获得先前的+ 1值。
#include <iostream>
using namespace std;
enum weekdays {
Sunday,
Monday = 2,
Tuesday,
Wednesday = 6,
Thursday,
Friday = 9,
Saturday = 12
};
int main(){
cout << "Sunday 值=" << Sunday << " Tuesday的值 = " << Tuesday;
return 0;
}
所有值都必须是整型常量
赋给enum元素的所有值都必须是整型常量。例如,它们应该在最小和最大可能整数的范围内。
元素作用域惟一
所有枚举元素或常量都应该具有惟一的作用域。这意味着一个元素不能成为同一程序中两个不同枚举的一部分,因为它将在编译期间失败。
enum Cars{Mahindra, Jeep, BMW};
enum Luxury_Cars{BMW, Ferrari, Mercedes_Benz};
int main(){
return 0;
}
编译失败
<source>:2:18: error: 'BMW' conflicts with a previous declaration
2 | enum Luxury_Cars{BMW, Ferrari, Mercedes_Benz};
| ^~~
<source>:1:27: note: previous declaration 'Cars BMW'
1 | enum Cars{Mahindra, Jeep, BMW};
| ^~~
scoped enum
简要说明
enum struct|class 名字 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } 声明底层类型为 int 的scoped enum 类型(关键词 class 与 struct 完全等价)
示例
#include <iostream>
using namespace std;
enum class Color { red, green = 20, blue };
int main() {
Color r = Color::blue;
switch (r) {
case Color::red:
std::cout << "红\n";
break;
case Color::green:
std::cout << "绿\n";
break;
case Color::blue:
std::cout << "蓝\n";
break;
}
return 0;
}
enum struct|class 名字 : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... } 声明底层类型为 类型 的scoped enum 类型
enum class Color:int { red=0, green = 20, blue=1 };
enum struct|class 名字 ; 前置声明 底层类型为 int 的scoped enum 类型的不可见枚举声明
enum struct|class 名字 : 类型 ; 前置声明 底层类型为 类型 的scoped enum 类型的不可见枚举声明
解决了三个问题
enum classes (new enums , strong enums ) 解决了传统c++枚举的三个问题:
- 常规枚举隐式转换为int,当有人不希望枚举充当整数时,会导致错误。
unscoped enum 类型中的枚举量可以隐式的转换为整型类别(并能够转换为浮点类型),scoped enum 类型中枚举量不能转换为其他类别。
enum color{
Red,
Green,
Yellow
};
enum class NewColor{
Red_1,
Green_1,
Yellow_1
};
int main()
{
int i = Red;
return 0;
}
- 传统枚举将枚举器超出到周围的作用域,导致名称冲突。
enum vehicle{
Car,
Bus,
Bike,
Autorickshow
};
enum FourWheeler{
Car,
SmallBus
};
enum class Editor{
vim,
eclipes,
VisualStudio
};
enum class CppEditor{
eclipes,
VisualStudio,
QtCreator
};
编译错误
<source>:11:5: error: 'Car' conflicts with a previous declaration
11 | Car,
| ^~~
<source>:3:5: note: previous declaration 'vehicle Car'
3 | Car,
| ^~~
- 不能指定枚举的基础类型,这会导致混淆、兼容性问题,并导致无法前置声明。
注:C++11后传统的枚举也可以被前置声明
#include <iostream>
using namespace std;
enum class Port : unsigned char;
class MyClass{
public:
void PrintPort(enum class Port p);
};
void MyClass::PrintPort(enum class Port p){
cout << (int)p << endl;
}
int main(){
MyClass a;
a.PrintPort(static_cast<Port>(1));
return 0;
}
枚举的大小
每个枚举类型都拥有底层类型,它可以是
- 显式指定(
scoped enum 和unscoped enum 均可) - 省略,该情况下对于有作用域枚举是
int ,或(对于无作用域枚举)是足以表示枚举所有值的实现定义的整数类型
#include <iostream>
#include <type_traits>
using namespace std;
enum class Color : char { red, blue };
enum class Color2 {
red,
yellow,
green
};
enum Color3 {
red,
yellow,
green
};
enum E {
E1 = 1,
E2 = 2,
Ebig = 0xFFFFFFF0U
};
enum EE : unsigned long {
EE1 = 1,
EE2 = 2,
EEbig = 0xFFFFFFF0U
};
int main() {
constexpr bool e1 = std::is_same_v< std::underlying_type_t<Color>, char >;
constexpr bool e2 = std::is_same_v< std::underlying_type_t<Color2>, int >;
constexpr bool e3 = std::is_same_v< std::underlying_type_t<Color3>, int >;
constexpr bool e4 = std::is_same_v< std::underlying_type_t<E>, int >;
constexpr bool e5 = std::is_same_v< std::underlying_type_t<EE>, unsigned long >;
std::cout
<< " color 底层类型 " << (e1 ? "char" : "non-char") << '\n'
<< " color2 底层类型 " << (e2 ? "int" : "non-int") << '\n'
<< " color3 底层类型 " << (e3 ? "int" : "non-int") << '\n'
<< " E 底层类型 " << (e4 ? "int" : "non-int") << '\n'
<< " EE 底层类型 " << (e5 ? "unsigned long" : "unsigned long") << '\n'
;
return 0;
}
运行结果如下:
color 底层类型 char
color2 底层类型 int
color3 底层类型 non-int
E 底层类型 non-int
EE 底层类型 unsigned long
优选scope enum 的三个理由
- 降低名字空间污染
- 枚举量的更强类别,从限定作用域的枚举类别到任何其他类别都不能隐士转换
scope enum 可以进行前置声明,而unscope enum 需要进行简单处理才能进行前置声明。
标准库的枚举
在标准库中,使用enum类
- 对于映射系统特定的错误代码:在标准库头文件
<system_error> ,如下:
enum class errc {
address_family_not_supported,
address_in_use,
address_not_available,
already_connected,
...
wrong_protocol_type,
};
- 指针安全指示器: 在
<memory> 中
enum class pointer_safety {
relaxed,
preferred,
strict
};
- pointer_safety::strict 只有安全派生的指针(指向以 new 分配对象或其子对象的指针)可解引用或解分配。垃圾收集器可能活跃。
- pointer_safety::preferred 认为所有指针均合法且可以解引用或解分配。基于可达性的泄露检测器可能活跃。
- pointer_safety::relaxed 认为所有指针均合法且可以解引用或解分配。
- 对于I/O流错误:位于
<iosfwd> enum class io_errc;(C++11 起)
带作用域枚举 std::io_errc 定义 I/O 流报告于 std::ios_base::failure 异常对象的错误码。只要求一个错误码( std::io_errc::stream ),虽然实现可以定义额外的错误码。因为提供了std::is_error_code_enum 的适当特化,故 std::io_errc 类型的值能隐式转换成 std::error_code 。
- 对于异步通信错误处理: 在
<future> 中
enum class future_errc {
broken_promise = ,
future_already_retrieved = ,
promise_already_satisfied = ,
no_state =
};
参考
[1] Enum in C: Understanding The Concept of Enumeration [2] Why is enum class preferred over plain enum? [3] Enumeration declaration [4] enum class – scoped and strongly typed enums [5] Why am I getting this ‘enum’ is not a class or a namespace error? [6] How to use enums in C++ [7] std::pointer_safety
|