栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Effective Modern C++[实践]->优先使用 scoped enums,而非 unscoped enums

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Effective Modern C++[实践]->优先使用 scoped enums,而非 unscoped enums

优先使用 scoped enums
  • 枚举
  • `unscoped enum`
    • 简要说明
    • 知识点位
      • 多个enum名称或元素可以有相同的值
      • 默认从0开始
      • 未赋值的前项+1
      • 所有值都必须是整型常量
      • 元素作用域惟一
  • `scoped enum`
    • 简要说明
    • 解决了三个问题
  • 枚举的大小
  • 优选`scope enum`的三个理由
  • 标准库的枚举
  • 参考

  • 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 
using namespace std;

enum Cars{Jeep = 1, BMW = 0, Mercedes_Benz = 0};
int main(){
  	cout<<"Jeep= "<#include 
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 
using namespace std;

enum weekdays {
    Sunday,
    Monday = 2,
    Tuesday,
    Wednesday = 6,
    Thursday,
    Friday = 9,
    Saturday = 12
};
int main(){
    cout << "Sunday 值=" << Sunday << "  Tuesday的值 = " << Tuesday;//Sunday 值=0  Tuesday的值 = 3
    return 0;
}
所有值都必须是整型常量

赋给enum元素的所有值都必须是整型常量。例如,它们应该在最小和最大可能整数的范围内。

元素作用域惟一

所有枚举元素或常量都应该具有惟一的作用域。这意味着一个元素不能成为同一程序中两个不同枚举的一部分,因为它将在编译期间失败。

enum Cars{Mahindra, Jeep, BMW};
enum Luxury_Cars{BMW, Ferrari, Mercedes_Benz};
int main(){
    return 0;
}

编译失败

:2:18: error: 'BMW' conflicts with a previous declaration
    2 | enum Luxury_Cars{BMW, Ferrari, Mercedes_Benz};
      |                  ^~~
:1:27: note: previous declaration 'Cars BMW'
    1 | enum Cars{Mahindra, Jeep, BMW};
      |                           ^~~
scoped enum 简要说明
  1. enum struct|class 名字 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... }
    声明底层类型为 int 的scoped enum类型(关键词 class 与 struct 完全等价)

示例

#include 

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;
}
  1. enum struct|class 名字 : 类型 { 枚举项 = 常量表达式 , 枚举项 = 常量表达式 , ... }
    声明底层类型为 类型 的scoped enum类型
enum class Color:int { red=0, green = 20, blue=1 };
  1. enum struct|class 名字 ; 前置声明
    底层类型为 int 的scoped enum类型的不可见枚举声明
  1. enum struct|class 名字 : 类型 ; 前置声明
    底层类型为 类型 的scoped enum类型的不可见枚举声明
解决了三个问题

enum classes (new enums, strong enums) 解决了传统c++枚举的三个问题:

  1. 常规枚举隐式转换为int,当有人不希望枚举充当整数时,会导致错误。

unscoped enum类型中的枚举量可以隐式的转换为整型类别(并能够转换为浮点类型),scoped enum类型中枚举量不能转换为其他类别。

enum color{
    Red,
    Green,
    Yellow
};
enum class NewColor{
    Red_1,
    Green_1,
    Yellow_1
};
int main()
{
    //! 隐式转换是可能的
    int i = Red;

    //! 需要enum类名后跟访问说明符。 Ex: NewColor::Red_1
    // int j = Red_1; // error C2065: 'Red_1': undeclared identifier

    //! 隐式转换是不可能的。Solution Ex: int k = (int)NewColor::Red_1;
    // int k = NewColor::Red_1; // error C2440: 'initializing': cannot convert from 'NewColor' to 'int'

    return 0;
}
  1. 传统枚举将枚举器超出到周围的作用域,导致名称冲突。
enum vehicle{
    Car,
    Bus,
    Bike,
    Autorickshow
};

enum FourWheeler{
    Car,        // car 已经在范围内被声明过,error C2365: 'Car': redefinition; previous definition was 'enumerator'
    SmallBus
};

enum class Editor{
    vim,
    eclipes,
    VisualStudio
};

enum class CppEditor{
    eclipes,       // No error of redefinitions
    VisualStudio,  // No error of redefinitions
    QtCreator
};

编译错误

:11:5: error: 'Car' conflicts with a previous declaration
   11 |     Car,        // error C2365: 'Car': redefinition; previous definition was 'enumerator'
      |     ^~~
:3:5: note: previous declaration 'vehicle Car'
    3 |     Car,
      |     ^~~
  1. 不能指定枚举的基础类型,这会导致混淆、兼容性问题,并导致无法前置声明。
    注:C++11后传统的枚举也可以被前置声明
#include 

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(1));
    return 0;
}
枚举的大小

每个枚举类型都拥有底层类型,它可以是

  1. 显式指定(scoped enum和unscoped enum均可)
  2. 省略,该情况下对于有作用域枚举是 int ,或(对于无作用域枚举)是足以表示枚举所有值的实现定义的整数类型
#include 
#include 

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, char >;
  constexpr bool e2 = std::is_same_v< std::underlying_type_t, int >;
  constexpr bool e3 = std::is_same_v< std::underlying_type_t, int >;
  constexpr bool e4 = std::is_same_v< std::underlying_type_t, int >;
  constexpr bool e5 = std::is_same_v< std::underlying_type_t, 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的三个理由
  1. 降低名字空间污染
  2. 枚举量的更强类别,从限定作用域的枚举类别到任何其他类别都不能隐士转换
  3. scope enum可以进行前置声明,而unscope enum需要进行简单处理才能进行前置声明。
标准库的枚举

在标准库中,使用enum类

  1. 对于映射系统特定的错误代码:在标准库头文件 ,如下:
enum class errc {
    address_family_not_supported,       // EAFNOSUPPORT
    address_in_use,                     // EADDRINUSE
    address_not_available,              // EADDRNOTAVAIL
    already_connected,                  // EISCONN
		...
    wrong_protocol_type,                // EPROTOTYPE
  };
  1. 指针安全指示器: 在 中
enum class pointer_safety {
     relaxed,
     preferred,
     strict
};
  • pointer_safety::strict 只有安全派生的指针(指向以 new 分配对象或其子对象的指针)可解引用或解分配。垃圾收集器可能活跃。
  • pointer_safety::preferred 认为所有指针均合法且可以解引用或解分配。基于可达性的泄露检测器可能活跃。
  • pointer_safety::relaxed 认为所有指针均合法且可以解引用或解分配。
  1. 对于I/O流错误:位于
    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 。

  1. 对于异步通信错误处理: 在中
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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1001644.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号