第6课 类型别名和强枚举类型

一. typedef和using关键字

(一)两者的差异

  ①C++11引入using关键字,覆盖了typedef的全部功能。它既可以用来定义类型的别名,也可以定义模板的别名。而typedef可以定义类型的别名,但不能用来重定义模板的别名。

  ②使用using不用写“::type”的后缀。在模板内,对于内嵌typedef的引用经常要加上typename前缀。

  ③using采用类似于赋值的方式,从语法比typedef更加清晰。

(二)using在模板中的优势

  ①using可以直接为模板取别名(alias template),但typedef需外加一个包装类才能达到类似的目的。

  ②在模板类部使用遇到依赖型类型时(如MyAlloc<T>::type),需要在类型前面用typename修饰,以表明它是一个类型,而不是数据成员。但使用using定义的模板别名则不会出现这个问题。

  ③许多C++11中的“类型萃取”是在包装类模板里用typedef来实现的(eg.std::xxx<T>::type)。而C++14是都加上了对应的别名模板(如std::xxx_t<T>)。

【编程实验】typedef和using的比较

#include <iostream>
#include <unordered_map>
#include <vector>
using namespace std;

//自定义内存分配器
template<typename T>
class MyAlloc {};  //用于演示,没有实际内容

//演示类
class Widget{};
template<typename T, typename Alloc = MyAlloc<T>>
class List
{
public:
    List(){}
    List(std::initializer_list<T> list){}
};

//2.1: 为模板取别名
template<typename T>
using MyList1 = List<T, MyAlloc<T>>;   //使用using方式,合法!

//template<typename T>
//typedef std::list<T, MyAlloc<T>> type;   //无法通过这种方式为模板取别名,必须加上一个包装类(如下)

template<typename T>
struct MyList2    //外包装类
{
    typedef List<T, MyAlloc<T>> type; //使用typedef方式
};

//2.2 依赖型类型(如list1)
template<typename T>
class Foo
{
public:

    //嵌套依赖类型名问题
    typename MyList2<T>::type list1;   //注意:MyList1<T>::type的类型,依赖模板形参T。在模板内定义时,必须加typename
                                       //因为,当编译器看到MyList1<T>::type时,它不能确定这是一个类型,还是一个数据成员变量?如果是成员变量,
                                       //则用其去声明一个list1变量,显然是错的。

    MyList2<Widget>::type list2;       //由于MyList1<Widget>::type是个确定的类型,不依赖模板形参T。因此其前面可以不加typename,但加了也不影响

    MyList1<T> list3;                  //MyList2<T>是用using声明的,因它是一个别名模板,必然也就是一个类型,所以不会出现二义/性。其前面无须加typename
};

//3. 类型萃取(C++11和C++14中的等价物)
template<class T>
using remove_const_t = typename remove_const<T>::type;  //const T --> T

template<class T>
using remove_reference_t = typename remove_reference<T>::type; //T&/T&& --> T

template<class T>
using add_lvalue_reference_t = typename add_lvalue_reference<T>::type; //T --> T&

int main()
{
    //1. typedef 和 using的等效定义
    typedef std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS1;  //typedef
    using UPtrMapSS2 = std::unique_ptr<std::unordered_map<std::string, std::string>>;   //赋值方式,更直观

    typedef std::vector<std::string> strVec1;
    using strVec2 = std::vector<std::string>;

    typedef void(*FP1)(int, const std::string&);
    using FP2 = void(*)(int, const std::string&); //使用using声明的函数指针

    //2. using在模板中的优势
    //2.1为模板取别名
    MyList1<Widget> lw1;        //使用using方式,直接使用模板别名!
    MyList2<Widget>::type lw2;  //使用typedef方式,需加"::type"后缀

    //2.2 嵌套依赖类型名问题(出现在模板类内部)
    Foo<Widget> foo;
    foo.list1 = { Widget(), Widget(), Widget() };
    foo.list2 = { Widget(), Widget(), Widget() };
    foo.list3 = { Widget(), Widget(), Widget() };

    return 0;
}

二、强枚举类型

(一)enum 和 enum class的差异

  1. enum:被称为不限范围的枚举类型枚举量的名字会泄漏到枚举类型所在的作用域,这意味着在此作用域内不能有其他实体取相同的名字。enum class属于限定作用域的枚举类型,也被称为“枚举类”枚举量仅在枚举类型内可见,它不会带来名称空间的污染。

  2. 不限范围的枚举类型可以隐式转换到整数,“枚举类”不能隐式转化为其他任何类型。

 (二)枚举量的底层存储类型

1. enum和enum class 都支持指定底层类型。不限定范围的枚举类型没有默认的底层类型,而限制作用域的枚举类型的默认底层类型为int

2. 限定作用域的枚举类型总是可以进行前置声明,而不限范围的枚举却只有在指定了默认底层类型的前提下才可以进行前置声明。

【编程实验】enum和enum class的区别

#include <iostream>
#include <vector>
#include <tuple>
using namespace std;

//测试函数:求x的质因数(用于演示,没真正的实现)
std::vector<std::size_t> primeFactors(std::size_t x) { return{}; }

//辅助函数,用于将enum class中的枚举量转为其底层类型输出
template<typename E>
constexpr auto toUType(E enumerator) noexcept  //注意这里使用编译期常量,同时不抛出异常
{
    return static_cast<std::underlying_type_t<E>>(enumerator);    //underlying_type用于萃取底层类型
}

int main()
{
    //1. enum 和 enum class的区别
    //1.1 名称空间的污染
    enum Color1 {black, white, red};      //三个枚举量的作用域与Color1相同
    //auto white = false;   //错误! white己在Color1中声明了。

    enum class Color2 {black, white, red}; //black、white、read的作用域仅限定在Color2内。

    Color1 c1 = white;          //Color1中的white,名称污染到整个main作用域
    Color2 c2 = Color2::white;  //ok
    auto   c3 = Color2::white;  //ok

    //1.2 强类型阻止隐式类型转换
    //不限范围的枚举类型
    if (c1 < 14.5) {   //ok,不限范围的枚举类型可以将c1隐式转换为double
        auto factors = primeFactors(c1); //计算c1的质因数
    }

    //限定范围的枚举类型
    //if (c2 < 14.5) {   //error,限定范围的枚举类型无法隐式转为double,但可用static_cast<double>(c2)进行强制转换
    //    auto factors = primeFactors(c2); //计算c2的质因数,需static_cast<double>(c2)强制转换
    //}

    //2. 枚举类型的底层类型
    //2.1 enum类型(为了节约内存,编译器通常会为枚举类型选用足够表示枚举量取值的最小底层类型。但某些情况下,编译器
    //会以空间换时间,这种情况下可能不会选择最小底层类型)
    enum Color3 {blue, yellow, green}; //底层类型为char
    enum Status{good = 0, failed = 1, incomplete = 100, corrupt = 200, audited = 500, indeterminate = 0xFFFFFFFF}; //int

    //2.2 enum class类型(底层默认为int类型)
    enum class State{ good = 0, failed = 1, incomplete = 100, corrupt = 200, audited = 500, indeterminate = 0x00FFFFFF};

    //2.3 为枚举类型指定底层类型
    enum Color4 : std::uint8_t; //不限范围的枚举类型,注意由于指定了底层类型(uint8_t),所以可以进行前置声明
    enum class Color5 : std::uint32_t {good = 0, failed = 1, indeterminate = 0xFFFFFFFF};

    //3. 不限范围枚举类型在tuple中使用的优势
    //3.1 对比enum和enum class在tuple的使用的优劣
    using UserInfo = std::tuple<std::string, std::string, std::size_t>; //客户信息:3个成员分别为姓名、电子邮件和声望值
    UserInfo uInfo = std::make_tuple("SantaClaus", "[email protected]", 80);

    enum UserInfoFields1{uiName, uiEmail, uiReputation};       //定义enum类型的枚举类型
    enum class UserInfoFields2{uiName, uiEmail, uiReputation}; //定义enum class类型的枚举类型

    auto name = std::get<0>(uInfo);        //取出姓名,但用索引值0表示,不直观!
    auto email = std::get<uiEmail>(uInfo); //取出uEmail,用不限范围的枚举类型,含义非常直观
    auto rep = std::get<static_cast<std::size_t>(UserInfoFields2::uiReputation)>(uInfo); //取出声望值,用enum class则需强转,不好用!
    cout << "name = "<< name << ",email = "<< email <<",Reputation = " << rep << endl;

    //3.2 改进enum class的使用,但与enum相比仍较繁琐(注意,因为std::get<Index>中的Index必须为编译期常量,因此toUType函数必须
    //是constexpr函数)
    auto name2 = std::get<toUType(UserInfoFields2::uiName)>(uInfo);      //姓名
    auto email2 = std::get<toUType(UserInfoFields2::uiEmail)>(uInfo);    //email
    auto rep2 = std::get<toUType(UserInfoFields2::uiReputation)>(uInfo); //期望值
    cout << "name = " << name2 << ",email = " << email2 << ",Reputation = " << rep2 << endl;

    return 0;
}
/*输出结果
name = SantaClaus,email = [email protected],Reputation = 80
name = SantaClaus,email = [email protected],Reputation = 80
*/

原文地址:https://www.cnblogs.com/5iedu/p/11255707.html

时间: 2024-09-28 04:36:11

第6课 类型别名和强枚举类型的相关文章

006.值类型、引用类型、枚举类型、字符串、冒泡排序

1.二维数组和交错数组 2.参数数组:params 数据类型[]数组名只能有一个参数数组,必须是最后一个参数必须是一堆数组 同时存在其他的重载方法,方法调用时优先调用参数最匹配的,没有直接匹配的参数列表时,才调用带有参数列表的方法 3.类型:值类型:整型 float double decimal bool char 枚举 结构引用类型:string 数组(Array)类 接口 委托 4.枚举访问修饰符 enum 枚举{值1,值2}枚举定义的位置:命名空间和类都可以转换:(枚举类型)Enum.Pa

如何定义一个基础类型为Byte的枚举类型。

1 internal enum Color : byte 2 { 3 White, 4 Red, 5 Green 6 }

浅谈java中的枚举类型(转)

用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl.... .现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,而且枚举提供了比常量更多的方法. public enum Color { RED, GREEN, BLANK, YELLOW } 用法二:switch JDK1.6之前的switch语句只支持int,char,enum类型,使用枚举,能让我们的代码可读性更强. enum Signal { GREEN, YELLOW, RED } pu

枚举类型小结

枚举语法:[public] enum 枚举名{ 值1, 值2 值3, ......} 枚举类型默认可以跟int类型相互转换,枚举类型跟int类型是兼容的. public enum QQState{ Online, OffLine, Leave, Busy, QMe} class Program{ static void Main(string[] args){ QQState state=QQState.Online; //枚举类型默认可以跟int类型相互转换,枚举类型跟int类型是兼容的. i

C语言--enum,typedef enum 枚举类型详解

原文:http://z515256164.blog.163.com/blog/static/32443029201192182854300/ 有改动 C语言详解 - 枚举类型 注:以下全部代码的执行环境为VC++ 6.0 在程序中,可能需要为某些整数定义一个别名,我们可以利用预处理指令#define来完成这项工作,您的代码可能是: #define MON 1 #define TUE 2 #define WED 3 #define THU 4 #define FRI 5 #define SAT 6

类型别名、auto类型说明符和decltype类型说明符初探

类型别名 类型别名顾名思义是某种类型的另一个名字,常用于简化类型,易于理解和使用. 传统方法是使用关键字 typedef .新标准规定使用别名声明(alias declaration)来定义类型别名. using zhengxing = int; 如果某个类型别名指代的是复合类型或常量,那么把它用到声明语句中往往会让人理解出错. typedef char *cstring;//cstring 是 char* 的别名 const cstring cstr = 0;//cstr 是指向 char 的

比你想象中还要强大的枚举类型

开发中枚举类型往往被用在可以一一列举的实例中,比如 enum Color{red,green,blue;}.但是可能你不会注意到它的更强大之处,比如如下问题看看你能作答吗 1.枚举类型可以有构造函数吗? 2.枚举类型可以实现接口.继承类吗? 3.枚举类型可以有static成员变量和方法吗?以及可否有实例变量和方法吗? 4.枚举类型可以被继承吗? ..........等(打开eclipse一试便知的知识) 其实enum类型就是一个特殊的java类,它几乎具有一个java类所具有的大部分功能.它更类

java枚举类型

jvm并不支持枚举类型,java中枚举类型是在编译器层面上实现的,先看如下代码: package demo.nio; public class EnumDemo { public static enum Color{ Red(20), Green(10), Blue(30); Color(int v){ this.value = v; } private int value; public int getValue(){ return this.value; } } public static

5.9 enum--支持枚举类型

enum模块提供了枚举类型的支持.枚举类型是由一个名称和一个统一值来组成,值是常量的值,它们之间可以通过名称进行比较和引用,还可以迭代访问. 5.9.1 模块内容 本模块主要定义了两种枚举类型:Enum和IntEnum.定义了一个装饰器函数unique(),以便保证所有枚举常量的唯一性. class enum.Enum 构造枚举常量的基类. 例子: #python 3.4 import enum class Color(enum.Enum): red = 1 green = 2 blue = 3