Sunday, 20. August 2017 05:44PM
# Item 9: Prefer alias declarations to typedefs.
# 条目9:宁愿使用alias declararion 替代 typedef

术语&概念:

  • typedef

Example
~~~c++
typedef std::unique_ptr<std::unordered_map<std::string, std::string» UPtrMapSS;
~~~

  • alias declaration

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实现的版本。