在C++的程序中,我们经常会看到特别长的类型名,比如std::map<int, std:: string>::const_iterator。为了让代码看起来更加简洁,往往会使用typedef为较长的类型名定义一个别名,例如:
typedef std::map<int, std::string>::const_iterator map_const_iter;
map_const_iter iter;C++11标准提供了一个新的定义类型别名的方法,该方法使用using关键字,具体语法如下:
using identifier = type-id其中identifier是类型的别名标识符,type-id是已有的类型名。相对于typedef,我更喜欢using的语法,因为它很像是一个赋值表达式,只不过它所“赋值”的是一个类型。这种表达式在定义函数指针类型的别名时显得格外清晰:
typedef void(*func1)(int, int);
using func2 = void(*)(int, int);可以看到,使用typedef定义函数类型别名和定义其他类型别名是有所区别的,而使用using则不存在这种区别,这让使用using定义别名变得更加统一清晰。如果一定要找出typedef在定义类型别名上的一点优势,那应该只有对C语言的支持了。
前面我们已经了解到使用using定义别名的基本用法,但是显然C++委员会不会因为这点内容就添加一个新的关键字。事实上using还承担着一个更加重要的特性——别名模板。所谓别名模板本质上也应该是一种模板,它的实例化过程是用自己的模板参数替换原始模板的模板参数,并实例化原始模板。定义别名模板的语法和定义类型别名并没有太大差异,只是多了模板形参列表:
template < template-parameter-list >
using identifier = type-id;其中template-parameter-list是模板的形参列表,而identifier和type-id是别名类模板型名和原始类模板型名。下面来看一个例子:
#include <map>
#include <string>
template<class T>
using int_map = std::map<int, T>;
int main()
{
int_map<std::string> int2string;
int2string[11] = "7";
}在上面的代码中,int_map是一个别名模板,它有一个模板形参。当int_map发生实例化的时候,模板的实参std::string会替换std::map<int, T>中的T,所以真正实例化的类型是std::map<int, std::string>。通过这种方式,我们可以在模板形参比较多的时候简化模板形参。
看到这里,有模板元编程经验的读者可能会提出typedef其实也能做到相同的事情。没错,我们是可以用typedef来改写上面的代码:
#include <map>
#include <string>
template<class T>
struct int_map {
typedef std::map<int, T> type;
};
int main()
{
int_map<std::string>::type int2string;
int2string[11] = "7";
}以上代码使用typedef和类型嵌套的方案也能达到同样的目的。不过很明显这种方案要复杂不少,不仅要定义一个int_map的结构体类型,还需要在类型里使用typedef来定义目标类型,最后必须使用int_map<std::string>::type来声明变量。除此之外,如果遇上了待决的类型,还需要在变量声明前加上typename关键字:
template<class T>
struct int_map {
typedef std::map<int, T> type;
};
template<class T>
struct X {
typename int_map<T>::type int2other; // 必须带有typename关键字,否则编译错误
};在上面这段代码中,类模板X没有确定模板形参T的类型,所以int_map<T>::type是一个未决类型,也就是说int_map<T>::type既有可能是一个类型,也有可能是一个静态成员变量,编译器是无法处理这种情况的。这里的typename关键字告诉编译器应该将int_map<T>::type作为类型来处理。而别名模板不会有::type的困扰,当然也不会有这样的问题了:
template<class T>
using int_map = std::map<int, T>;
template<class T>
struct X {
int_map<T> int2other; // 编译成功,别名模板不会有任何问题
};值得一提的是,虽然别名模板有很多typedef不具备的优势,但是C++11标准库中的模板元编程函数都还是使用的typedef和类型嵌套的方案,例如:
template<bool, typename _Tp = void>
struct enable_if { };
template<typename _Tp>
struct enable_if<true, _Tp>
{ typedef _Tp type; };不过这种情况在C++14中得到了改善,在C++14标准库中模板元编程函数已经有了别名模板的版本。当然,为了保证与老代码的兼容性,typedef的方案依然存在。别名模板的模板元编程函数使用_t作为其名称的后缀以示区分:
template<bool _Cond, typename _Tp = void>
using enable_if_t = typename enable_if<_Cond, _Tp>::type;本章介绍了使用using定义类型别名的方法,可以说这种新方法更符合C++的语法习惯。除此之外,使用using还可以定义别名模板,相对于内嵌类型实现类似别名模板的方案,该方法更加简单直接。建议读者在编译环境允许的情况下尝试使用using来定义别名。