Item 9: 比起typedef更偏爱别名注明(alias declaration)

正文翻译自modern effective
C++,由于水平有限,故不可以担保翻译完全正确,欢迎指出错误。谢谢!

博客已经搬至这里啦

自己坚信大家都同意下STL容器是一个吓主意,并且自己梦想当Item
18
中能让你相信使用std::unique_ptr为是一个吓主意,可是自猜,我们面临绝非此外一个人记念多次写这么的花色:“std::unique_ptr<std::unordered_map<std::string, std::string>>”。光是想想就感觉到,得“腕管综合症”的风险会增多。

为防止这样的医及之喜剧很简单,引进typedef

typedef
    std::unique_ptr<std::unordered_map<std::string, std::string>>
UPtrMapSS;

但是typedef太太太C++98了。当然,它们可以当C++11着行事,但是C++11乎提供了号声明(alias
declaration):

using UPtrMapSS =
    std::unique_ptr<std::unordered_map<std::string, std::string>>;

给出的typedef和别名讲明(alias
declaration)做的凡平等的作业,这值得大家去考虑这里是不是有什么科学的由来为大家更偏爱其中一个。

正确,不过以自家表明往日,我眷恋说一下另外:很五人发觉在关系函数指针的时光,别名阐明(alias
declaration)更好接受一些:

//FP 是指向一个函数的指针的别名,这个函数以一个int和一个
//const std::string&为参数,不返回任何东西。
typedef void (*FP)(int, const std::string&);    //typedef

//和上面同样的意义
using FP = void(*)(int, const std::string&):    //别名声明

自然,这半独还挺容易接受,并且不管怎么说,很少人索要花费大量之年华学下函数指针类型的号,所以这几乎不是一个强硬的理来为咱采纳别名注解(alias
declaration)替换typedef

然强大的说辞是存的:template。尤其是,别名声明(alias
declaration)能模板化(我们称为别超级模特板(alias
template)),不过typedef未克如此做。这给了C++11程序员一个概括的编制来发布在C++98中务必动嵌套在模板化struct中的typedef来开的作业。举个例子,考虑定义一个链表的别名,这个链表使用从定义的分配器(allocator)MyAlloc。使用别名讲明(alias
declaration),那就是略case:

//MyAllocList<T>就是std::list<T, MyAlloc<T>>的别名
template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;

MyAllocList<Widget> lw;     //客户代码

使用typedef,你虽然起麻烦了:

//MyAllocList<T>::type就是std::list<T, MyAlloc<T>>的别名
template<typename T>
struct MyAllocList{
    typedef std::list<T, MyAlloc<T>> type;
};

MyAllocList<Widget>::type lw;   //客户代码

如您想当template中使用typedef来上
使用template参数作为链表的模版类型参数
来创立别名的目标,这将更换得又不好,你必须以typedef前方加上typename

//Widget中有一个MyAllocList<T>作为成员变量
template<typename T>
class Widget{
private:
    typename MyAllocList<T>::type list;
    ...
};

这里,MyAllocList::type引用的是一个档次因让template的参数(T)。MyAllocList::type因而是一个依型(dependent
type),并且C++的不在少数”可爱“的平整中的一个便是,在乘型前面必须加typename

如果MyAllocList叫定义也别有名的模特板(alias
template),对于typename的需就烟消云散了(笨重的“::type”后缀也一去不返了):

template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;

template<typename T>
class WidgetP
private:
    MyAllocList<T> list;    //没有"typename",没有"::type"
    ...
};

对于你,MyAllocList(也就是,使用alias
template)可能拘留起和MyAllocList::type(也就是是,使用嵌套typedef)一样,也凭借让template参数(T),不过你免是编译器,当编译器处理Widget
template,并且境遇MyAllocList的使用(也就是,使用alias
template)时,它知道MyAllocList不畏是连串的名,因为MyAllocList大凡一个alias
template:它一定代表一个列。MyAllocList所以是一个非依赖类型(non-dependent
type),并且她既是无欲为无同意使用typename

单,当编译器在Widget
template中看到MyAllocList::type(也不怕是,使用嵌套typedef)时,编译器不必然她就表示一个门类,因为编译器不精晓此是不是发一个特化的MyAllocList,它的MyAllocList::type意味着的匪是一个类。这任起颇疯狂,然则对这可能性,不要怪编译器。我们人类精通怎么制作那样的代码。

举个例证,一些误入歧途的神魄可能像这么吃制作出:

class Wine{...};

template<>                  //特化的MyAllocList
class MyAllocList<Wine>{    //特化的类型是Wine
private:
    enum class WinType      //关于"enum class"的信息
    { White, Red, Rose};    //请看Item 10

    WineType type;          //在这个类中,type是一个成员变量
    ...
};

便像而看到的,MyAllocList::type免是一个门类,假使WidgetWine来实例化,Widget
template中的MyAllocList::type对一个成员变量,而未是路。由此,在Widget
template中,MyAllocList::type是否代表一个列需要依靠让T大凡啊,并且顿时为就是是干吗而您当这是一个体系,编译器如故坚定不移叫您以前头使用typename

比方你开过其余模板元编程(template metaprogramming
(TMP)),你早晚遭受过这样的需要:使用template类型参数创设修改后底类型。举个例子,对于给定的门类T,你也许想如果去丢T上的const或引用性。比如,你可能想假若把const
std::string&
变成std::string。或者你或许想使追加const给一个品类,或者将它们成为一个左值引用。比如,把Widget变成const
Widget
或者Widget&。(假使你莫明了任何TMP,这顶糟糕了,因为只要您想使改成一个实在决定的C++程序员,你足足需要熟稔C++在立时上头(TMP)的根基。在Item
23与27惨遭,你能顾TMP的例子,包含我提及的类型转换。)

以峰文件之各类模板被,C++11受了卿工具,让您为type
traits的样式来举行这个易。头文件被爆发众多type
traits,但无是均用来做类型转换的,不过它提供部分可预测的接口,给起一个君想换的种T,结果的路就是std::transformation::type(std::转换::type):

std::remove_const<T>::type          //用 const T 产生 T

std::remove_reference<T>::type      //用 T& 或 T&& 产生 T

std::add_lvalue_reference<T>::type  //用 T 产生 T&

注只是总括了这一个易做了哟,所以利用时毫可是度随便。在工程被使用它前,我明白你会晤先看一下参考手册的。

不管怎么说,我之目标不是吃你一个type
traits的导,而是强调这一个易在利用时需要在后写上“::type”。倘诺您以template中拿它运于template类型参数(你平常需要以代码中如此用),你还要当后边加上typename。原因是,在C++11底type
traits中,这一个语法使用嵌套于模板化struct中的typedef以。好的,就是因这么的号实现之技能,我思给你知其不如别有名的模特板(alias
template)!;

C++11挨这样实现是出历史由来之,不过咱不去研讨她(我包这生低俗),因为C++标准委员会特别晚才认识到alias
templates才是无比好之做法,并且于C++11丁的有所转换,他们当C++14饱受蕴藏了具备的alias
template版本。所有的别名都有一致的花样:每个C++11的更换std::transformation::type,在C++14遭到一样有alias
template以std::transformation_t命名。例子将表明自己说的:

std::remove_const<T>::type          //C++11: const T -> T
std::remove_const_t<T>              //C++14 等价的操作

std::remove_reference<T>::type      // C++11: T&/T&& → T
std::remove_reference_t<T>          // C++14 等价的操作

std::add_lvalue_reference<T>::type  // C++11: T → T&
std::add_lvalue_reference_t<T>      // C++14 等价的操作

C++11版的换在C++14挨如故有效,可是本人未亮你暴发什么理由去接纳其。甚至要您从未行使C++14,自己写一份alias
template尽管比如娱乐同样。只待C++11之言语特色,甚至连小孩还是可以够模拟这种情势,是吧?假若您正来同等份C++14标准的电子稿,将变得愈简便易行,因为固然开的从事就只有拷贝和糊。这里,我叫你一个起头:

template <class T>
using remove_const_t = typename remove_const<T>::type;

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

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

探望了邪,没有于这更简单的从事了。

            你一旦切记的从
  • typedef不匡助模板化,可是别名表明(alias declaration)辅助。

  • alias
    templates避免了“::type”后缀,以及以template中“typename”前缀(当代表一个类型时)的运。

  • C++14资具有C++11 type traits 转换的alias templates版本。