优先选择别名声明,而非typedef
typedef
用法:
typedef
std::unique_ptr<std::unordered_map<std::string, std::string>> UPtrMapSS;
using
用法(别名声明):
using UPtrMapSS = std::unique_ptr<std::unordered_map<std::string, std::string>>;
别名声明可以模板化(这种情况下,它们被称为别名模板),typedef
就不行。
比如,想要定义一个同义词,表达一个链表,它使用了一个自定义分配器MyAlloc
。
template<typename T>
using MyAllocList = std::list<T,MyAlloc<T>>;
MyAllocList<Widget> lw;
如果使用typedef
的话
template<typename T>
struct MyAllocList{
typedef std::list<T, MyAlloc<T>> type;
};
MyAllocList<Widget>::type lw;
如果想在模板内使用typedef
来创建一个表,它容纳的对象类型由模板形参指定的话,那你就要给typedef
前缀。
template<typename T>
class Widget{
private:
typename MyAllocList<T>::type list;
};
这里,MyAllocList<T>::type
代表一个依赖于模板类型形参T
的类型,所以MyAllocList<T>::type
称为带依赖类型,而c++中的规则就是带依赖类型前面必须加个typename
。
如果MyAllocList
是使用别名模板来定义的,那么要写typename
的要求就消失了。
template<typename T>
using MyAllocList = std::list<T, MyAlloc<T>>;
template<typename T>
class Widget{
private:
MyAllocList<T> list;
};
当编译器处理到Widget模板并遇到了MyAllocList<T>
时,它们知道MyAllocList<T>
是一个类型的名字,因为MyAllocList<T>
是个非依赖性类型,所以typename
饰词既不要求也不允许。
当编译器遇到了Widget
模板中的MyAllocList<T>::type
时,它们不可能确定MyAllocList<T>::type
命名了一个类型,因为可能在MyAllocList
的某个特化中,MyAllocList<T>::type
表示并非类型而是其他的什么东西。
class Wine{};
template<>
class MyAllocList<Wine>
{
enum class WineType{ White,Red,Rose };
WineType type; //在这个类中,type是个数据成员
...
};
如果Widget
模板采用Wine
类型进行实现,则其中的MyAllocList<T>::type
就表示一个数据成员而非类型。
编译器要求在前面加一个typename
,是为了判断Widget
模板中的MyAllocList<T>::type
究竟是否表示一个类型。
c++11
以类型特征的形式给程序员以执行此变换的工具。
类型特征是在头文件<type_traits>
给出的一整套模板。该头文件中有几十个类型特征,它们并非都是执行类型变换功能的用途,也包含了一些可于此的接口。
对给定待变换类型T
,其结果类型为std::transformation<T>::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&
如果想将它们应用与模板内的类型形参,那就还是要每次都在前面加上typename
。这两个词法存在的原因是,C++11中的类型特征是用嵌套在模板化的struct
里的typedef
实现的。
要点速记
typedef
不支持模板化,但是别名声明支持- 别名模板可以让人免写
::type
后缀,并且在模板中,对于内嵌typedef
的引用经常要求加上typename
前缀