C/C++编程:强类型枚举

1060 篇文章 296 订阅

有缺陷的枚举类型

C++规定,具名的enum类型,以及成员的名字都是全局可见的。

  • 这与C++具名的namespace,class/struct、union必须通过名字::成员名的方式访问相比各个不入(namespace等被称为强作用域类型,而enum是非强作用域类型)。
  • 导致同一个命名空间中的不同枚举类型的枚举值不能相同

一不小心就很容易出现问题,比如下面:
在这里插入图片描述
上面Category和Type都是全局的名字,因此编译会报错。

#include <iostream>
#include <type_traits>

namespace T{
    enum Type{
        General,Light,Medium,Heavy
    };
}

namespace {
    enum Category{
        General,Pistal, MachineGun
    };
}

int main(){
    T::Type t = T::General;
    if (t == General){  // 实际上不同
        printf("some");
    }
}

上面,Category在一个匿名的namespace 中,所以所有的枚举成员名都默认进行全局名字空间。一旦程序员在检查t的值时忘记使用namespace T了,就会出现错误的结果:

在这里插入图片描述
另外,由于C中枚举被设计为常量数值的”别名“,所以枚举的成员总是可以被隐式的转换为整形。会让两种完全不同的枚举类型可以进行直接的比较(虽然编译器给出了检查,但并非所有)。 很多时候,这也是不安全的。

还有,枚举类型所占用的空间大小也是一个”不确定“值。标准规定,C++枚举所基于的”基础类型“是由编译器来具体指定实现的,这会导致枚举类型成员的基本类型的不确定性问题(尤其是符号性)。比如:

#include <iostream>
#include <type_traits>

enum C{ C1 = 1, C2 = 3};
enum D{ D1 = 1, D2 = 3, DBig = 0xfffffff0u};
enum E{ E1 = 1, E2 = 3, EBIG = 0xffffffffll};
int main(){
    printf("%lu\t", sizeof(C)); // 4
    printf("%lu\t", DBig); //不同的编译器输出不同

}

对于不同的编译器,上面DBig的输出结果不同,VS中是-16,g++是4294967280。这是由于VS总是使用无符号类型作为枚举的底层实现,而g++会根据枚举的类型进行变动造成的。

解决方案

非强类型作用域,允许隐式转换为整型,占用存储空间以及符号性的不确定,都是没觉类的确定。针对这些缺点,C++11引入了枚举类(enumeration class, 也叫做强类型枚举)。

声明方式:使用enum class的语法进行声明:

enum class new_enum : unsigned int{
    val1,
    val2,
    val3 = 100,
    val4 = 100
};

在这个语法中,枚举类型后面使用了冒号以及类型关键字来指定枚举中的枚举值的类型,因为我们可以为枚举赋值(没有指定时将默认使用int)

强类型枚举的优势:

  • 强作用域:强类型枚举成员的名称不会被输出到其父作用域空间。 也也导致了我们在使用类型成员的时候必须加上所属的枚举类型的名字。

  • 可以指定底层类型。强类型枚举默认的底层类型是int,但也可以显式的指定底层类型,具体的方法是在枚举类型的名称后面加上”:Type“,其中type可以是除了wchar_t之外的任何整形,比如:

enum class Type:char{ Generay, Light, Medium, Heavy};
  • 转换限制:
    • 强类型枚举成员的值不可以与整形隐式的相互转换。同时也不能将其与整数数字进行比较,更不能对不同的枚举类型的枚举值进行比较。但相同枚举值之间如果指定的值相同,那么可以进行比较:
      在这里插入图片描述
    • 如果希望获得枚举值的值时,将必须显式的进行类型转换,我们可以通过重载<<来进行输出:
template <typename  T>
std::ostream& operator<<(typename std::enable_if<std::is_enum<T>::value, std::ostream>::type& stream, const T& e)
{
    return stream << static_cast<typename std::underlying_type<T>::type>(e);
}
这时,下面的代码将能够被编译:
    std::cout << new_enum ::val1 << std::endl;

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值