条款09:优先选用别名声明,而非typedef
目录
条款09:优先选用别名声明,而非typedef(Prefer alias declarations to typedef)
共有功能:普通类型、函数指针类型
特有功能:模板、命名空间
深入理解类型特征(type trait)
条款09:优先选用别名声明,而非typedef(Preferalias declarationstotypedef)
作用:两者都是“定义类型的别名”,而不是“定义/创建新的类型”。
共有功能:普通类型、函数指针类型
结论1:在定义“普通类型”的别名时,两者的使用效果(可读性)差不多。
- typedef:
- 语法:typedef 类型原名类型别名;
- 记忆:将类型原名“定义为/变为”类型别名
- using:
- 语法:using类型别名= 类型原名;
- 记忆:将类型原名“赋值给”类型别名
- 注意:既然“typedef”和“using别名声明”完成的工作是一模一样的,那就有理由考虑是否有坚实的技术理由来说明应该优先选用其中一个,而非另一个。
结论2:在定义“函数指针类型”的别名时,“using别名声明”比“typedef”具有更强的可读性。
- typedef:第一眼很难看出FP是一个别名,其本质是一个函数指针,指向的函数带有int和const std::string&两个形参,不返回任何东西。
- using:把别名的名字强制分离到了左边,而把别名对应的实际类型放在了右边,比较直观清晰,可读性比较好。
- 注意:上述两个形式都不那么好理解,而且花大量时间和函数指针定义别名的人毕竟少数。因此,以上情况很难构成选用“using别名声明”而非“typedef”的压倒性理由。
特有功能:模板、命名空间
结论3(重要):“using别名声明”可以模板化(别名模板,alias template)(即给模板定义别名),而“typedef”不行。别名模板可以避免使用“::type”后缀,以及在模板中的“typename”前缀。
它给予C++11程序员一种直截了当的表达机制,用以表达C++98程序员不得不用“嵌套在模板化的struct里的typedef”才能硬搞出来的东西。
- 场景描述:定义“别名”,用以表示一个“链表”(使用自定义分配器MyAlloc);并使用“别名”分别在模板外和模板内创建一个“链表”。
- typedef:需要添加外敷类(尽管可以实现,但是非常麻烦,不建议)
使用(模板外):需要添加后缀“::type”
使用(模板内):还需要添加前缀“typename”(嵌套从属类型名称)(在模板内,使用typedef创建链表,它容纳的对象类型由模板参数指定)
- using:小菜一碟
使用(模板外):不需要添加后缀“::type”
使用(模板内):也不需要添加前缀“typename”
结论4:“using别名声明”可以声明命名空间,而“typedef”不行。
- using namespace std;
- 防止命名冲突
- 可以直接使用命名空间中定义的类
结论5:继承构造函数、调用父类中被子类覆盖的同名函数
深入理解类型特征(type trait)
需求分析:只要你曾经接触过哪怕一点点模板元编程(Template Metaprogramming, TMP),几乎都曾遇到过这样一种需求:从模板的类型参数出发,创建出该类型的某种修正形式。举个例子,给定某类型T,你可能会需要去掉T的所有const和引用修饰符(把const std::string&变换成std::string);又或者你可能想给一个类型加上const或变为左值引用(把Widget变换成const Widget或Widget&)。(“条款23”和“条款27”可以看到TMP的实战实例,包括上述提及的几种类型转换)。
C++11实现:以“类型特征(type traits)”的形式提供了执行这些类型变换的工具。类型特征是一组由头文件<type_traits>定义的模板工具集合。该头文件包含数十种类型特征,它们的用途并不完全相同:有些用于检测类型属性,有些用于类型判断,而其中一部分则专门用于执行类型变换,并为此提供了统一且可预测的接口。
- 变换工具:对给定待变换类型T,结果类型为std::transformation<T>::type
- 问题分析:首先,在使用这些类型变换时,需要添加“::type”后缀。其次,若想将“类型变换工具”应用在模板内的类型参数(实际代码中几乎总是如此),还需要添加“typename”前缀。之所以存在这两个繁琐语法,是因为C++11中类型特征的实现方式:通过“嵌套在模板化struct内部的typedef”来实现(前文已表明不要使用,因为它不如别名模板)。
C++14实现:为C++11中的所有类型变换都加上了对应的别名模板。这些别名有一个公共形式:每个C++11中的变换std::transformation<T>::type,都有一个C++14中名为std::transformation_t<T>的对应别名模板。
尽管C++11的形式在C++14中依然是合法的,但很难理解为什么仍然坚持使用它们。即便你的编译环境暂时无法支持C++14,自己实现一个别名模板其实也并不困难。所需的不过是一些C++11已经提供的语言特性,实现起来相当直接,几乎任何人都可以轻松仿写。如果手边还有一份C++14标准的电子版,那就更加简单了,基本上只需要照着标准中的定义复制粘贴即可。
在C++11之后,许多代码规范建议优先使用using而不是typedef,证明了在实际应用和代码维护中,using更具有优势。
