[C++] enum 以及 enum class 简单用法

    C语言风格枚举类型 enum

    这是一个enum类型

    enum LogType {
    	OK,
    	Info,
    	Error
    };
    

    可以像这样指定enum的基础类型(默认类型是int),用英文来说就是underlying type

    enum LogType : unsigned int {
    	OK,
    	Info,
    	Error
    };
    

    继续升级,指定每个枚举元素具体的值

    enum LogType : unsigned int {
    	OK = 0x00,
    	Info = 0x01,
    	Error = 0x02
    };
    

    经典位掩码,即Bitmask写法

    enum WindowFlags : unsigned int {
        None = 0x0000,
        NoMove = 0x0001,
        NoResize = 0x0002,
        NoSavedSettings = 0x0004
    };
    auto MySettings = NoMove | NoResize;
    

    但是enum本质上其实是无范围枚举,即unscoped enumeration,很容易就会发现命名冲突问题

    enum LogType {
    	OK,
    	Info,
    	Error
    }; // OK
    
    enum StatusCode {
        OK,
        Forbidden
    }; // OK?
    
    auto res = LogType::OK; // Compiler doesn't know which OK to use even using :: to specify
    

    想要解决这个问题并不难,我们可以外面套一层struct或者class

    struct LogType {
        enum {
            OK,
            Info,
            Error
        };
    };
    
    class StatusCode {
        enum {
            OK,
            Forbidden
        };
    };
    
    auto res = LogType::OK; // OK!
    

    但是这样似乎不太优雅,尤其是我们想将enum作为函数返回值的时候

    struct StatusCode {
        enum {
            OK,
            Forbidden
        };
    };
    
    StatusCode foobar() {
        return StatusCode::OK; // Hey, don't do this!
    };
    

    C++风格枚举类型 enum class

    为了解决以上种种问题,C++引入了enum class,这是一种范围枚举,即scoped enumeration,不存在命名冲突
    用法和enum相似,无非就是在后面添加了一个struct或者class(等效)

    enum struct LogType {
        OK,
        Info,
        Error
    };
    
    enum class StatusCode {
        OK,
        Forbidden
    };
    

    我们也可以像enum一样把enum class类型作为函数返回值(可以把它看作是一个类)

    enum class StatusCode {
        OK,
        Forbidden
    };
    
    StatusCode foobar() {
        return StatusCode::OK;
    };
    

    值得注意的是,enum class并不支持隐式类型转换,即implicit type conversion
    因此,enum class也并不能像enum那样直接进行位运算

    enum class WindowFlags : unsigned int {
        None = 0x0000,
        NoMove = 0x0001,
        NoResize = 0x0002,
        NoSavedSettings = 0x0004
    };
    
    WindowFlags foobar() {
        return WindowFlags::None;
    };
    
    if (!foobar())
    	exit(EXIT_FAILURE); // g++: expression must have bool type (or be convertible to bool)
    
    auto MySettings = NoMove | NoResize; // g++: no operator "|" matches these operands
    
    auto MySettings = static_cast<WindowFlags> (
        static_cast<unsigned int> (WindowFlags::NoMove) | static_cast<unsigned int> (WindowFlags::NoResize)); // OK
    

    显示类型转换,即explicit type conversion似乎过于臃肿,但是我们可以重载一下enum class相关的operator,以下模板放在我们的enum class的命名空间内即可(具体细节此处不赘述,请自行查阅std::enable_if_t, std::is_enum_v)

    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator& (enum_type x, enum_type y) {
        return static_cast<enum_type>
            (static_cast<cast_type> (x) & static_cast<cast_type> (y));
    }
    
    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator| (enum_type x, enum_type y) {
        return static_cast<enum_type>
            (static_cast<cast_type> (x) | static_cast<cast_type> (y));
    }
    
    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator^ (enum_type x, enum_type y) {
        return static_cast<enum_type>
            (static_cast<cast_type> (x) ^ static_cast<cast_type> (y));
    }
    
    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator~ (enum_type x) {
        return static_cast <enum_type> 
            (~static_cast<cast_type> (x));
    }
    
    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator&= (enum_type &x, enum_type y) {
        x = x & y;
        return x;
    }
    
    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator|= (enum_type &x, enum_type y) {
        x = x | y;
        return x;
    }
    
    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, enum_type> operator^= (enum_type &x, enum_type y) {
        x = x ^ y;
        return x;
    }
    

    enum class严格意义上来说并不是一个常规意义上的class,所以无法通过重载自动转换为bool类型,但是我们可以手写一个any函数来判断位掩码

    template <typename enum_type, typename cast_type = std::size_t>
    constexpr std::enable_if_t<std::is_enum_v<enum_type>, bool> any(enum_type x) {
        return static_cast<cast_type> (x) == 0;
    };
    

    简单样例(已载入上述模板)

    enum class WindowFlags : unsigned int {
        None = 0x0000,
        NoMove = 0x0001,
        NoResize = 0x0002,
        NoSavedSettings = 0x0004
    };
    
    auto MySettings = WindowFlags::NoMove | WindowFlags::NoResize;
    
    if (any(MySettings)) { // To check if any flags are set
    	/* do something */
    }
    

    参考链接

    cppreference.com

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值
    程序员都在用的中文IT技术交流社区

    程序员都在用的中文IT技术交流社区

    专业的中文 IT 技术社区,与千万技术人共成长

    专业的中文 IT 技术社区,与千万技术人共成长

    关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

    关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

    客服 返回顶部