Sunday, 20. August 2017 05:44PM
# Item 9: Prefer alias declarations to typedefs.
# 条目9:宁愿使用alias declararion 替代 typedef
术语&概念:
Example
~~~c++
typedef std::unique_ptr<std::unordered_map<std::string, std::string» UPtrMapSS;
~~~
Example
~~~c++
using UPtrMapSS = std:unique_ptr<std::unordered_map<std::string, std::string»;
~~~
## 快速笔记:
### 至少有两种理由这样做,其中一种属于个人喜好的问题,另一种令人信服
1. 当处理函数指针的时候,alias declaration 形式更加自然可读
2. alias declaration 特殊的地方在于它可以被模板化,但是typedef不能。
==Example 1==
~~~c++
typedef void (*FP)(int, const std::string&); //typedef
using FP = void (*)(int, const std::string&); //alias declaration
~~~
==Example 2==
考虑为拥有自定义分配器(allocator)的链表定义一个同名。注意这里的自定义分配器名为MyAlloc。Widget这里表示某一个用户定义的类。
以下是alias declaration的用法
~~~c++
template
using MyAllocList = std::list<T, MyAlloc>;
MyAllocList lw; //client code
~~~
想要使用**typedef**达到类似的效果就需要写成如下的样子
~~~c++
template
struct MyAllocList {
typedef std::list<T,MyAlloc> type;
};
MyAllocList::type lw; //client code
~~~
继续,当我们希望将上述那样一个链表用作某个模板类的成员时,事情会变得更加糟糕,
实现将会是下面这个样子
~~~c++
template
class Widget {
private:
typename MyAllocList::type list;
};
~~~
注意到这里的type是一个有依赖的类型(dependent type), 如果某个模板中的类型声明中包含了模板参数T我们一般称呼这是个dependent type,这种类型之前一定要写上typename编译才能知道这里表达的是一个类型而不是别的什么东西, typename的这种用法请参考EC++。这里作简单说明。
为什么这里的type叫做有依赖的类型呢,因为他具体是什么东西取决于依赖于T到底是什么。这个现象实质上是由模板的特化(specialization)引起的。
考虑这样的情况,假设我们的MyAllocList存在如下的特化
~~~c++
class Wine { ... }
template<>
class MyAllocList {
private:
enum class WineType
{ White, Red, Rose };
WineType type; //in this class, type is a data member!
…
};
~~~
可以看到这种情况下type并不表示为一个类型。正是有这样的原因存在,当定义一个模板时, 所有在声明形式中嵌入了模板参数T的类型声明前必须加入typename修饰词以显式地告诉编译器这是个类型,然后编译器才能正常工作。
然后我们来看看alias declaration在这种使用场景下的例子
~~~c++
template
using MyAllocList = std::list<T, MyAlloc>; //as before
template
class Widget {
private:
MyAllocList list; //no "typename", no "::type"
...
};
~~~
这里编译器在看到MyAllocList就已经清楚得知道它是个同名模板,必定表达的是个类型。清晰明了。
额外的例子
当我们在进行模板元编程时(template metaprogramming, TMP), 一定会有改变模板形参T的类型的需求,比如去除const-或是reference-修饰词,或者是增加const-或是使其转变为左值引用(lvalue reference)。
C++11在头文件中提供了一组模板工具来完成这些功能。它们都拥有
~~~c++
std::transformation::type
~~~
这样的形式。
==Example==
~~~c++
std::remove_const::type // yields T from const T
std::remove_reference::type // yields T from T& and T&&
std::add_lvalue_reference::type // yields T& from T
~~~
没错,由于一些历史原因,以上实现全部基于typedef。同样的功能,在C++14中提供了另一组形式为
~~~c++
std::transformation_t
~~~
的实现。例子如下
~~~c++
std::remove_const::type // C++11: const T -> T
std::remove_const_t // C++14 equivalent
std::remove_reference::type // C++11: T&/T&& -> T
std::remove_reference_t // C++14 equivalent
std::add_lvalue_reference::type // C++11: T -> T&
std::add_lvalue_reference_t // C++14 equivalent
~~~
以下是C++14标准实现,即使当我们只能使用C++11,我们也可以很容易复制粘贴这样的实现。
~~~c++
template
using remove_const_t = typename remove_const::type;
template
using remove_reference_t = typename remove_reference::type;
template
using add_lvalue_reference_t = typename add_lvalue_reference::type;
~~~
看,不能更简单了。
谨记要点
- typedef不支持模板化,alias declaration支持。
- 在模板中作类型声明时,alias declaration可以避免那些typedef总是需要的”::type”和”typename”
- C++11提供了类型特性(type traits)转化, C++14兼容前者的基础上又提供了所有的基于alias declaration实现的版本。