在C++98/03里,我们可以通过typedef 关键字定义一个类型的别名,比如 typedef unsigned int uint_t;在这里我们定义了unsigned int类型的别名和uint_t,在以后需要使用unsigned int的时候我们都可以用uint_t替换,但是uint_t仅仅是作为unsigned int的一个别名,如下的定义是不合法的:
typedef unsigned int uint_t;
void func(unsigned int);
void func(uint_t);
上面的func函数是一个不合法的函数重载,虽然使用typedef定义一个类型的别名很方便,但是typedef在使用上存在一些限制,比如说typedef无法重定义一个模板的别名。
考虑下面例子,我们在实际编程中经常使用到STL中的MAP,我们MAP的string类型的数据作为MAP的key,我们想根据STRING类型的KEY映射为一个String,int,long等类型的数据.
typedef std::map map_t;
//...
typedef std::map < std::string, std::string > map_str;
//...
如果需要映射成10中类型的数据,我们就需要利用typedef定义10个具体类型的别名,但是考虑到MAP的key值始终是变的,我们是否像下面一样可以用typedef+模板来定义一个别名呢
template
typedef std::map map;
map map_i;
map map_str;
遗憾的是上述的定义不能通过编译,也就是C++ 98/03并不支持这样的操作,而通常是通过一个包裹类的方式来实现上述的需求:
template
struct alias_map
{
typedef std::map map;
};
alias_map::map map_t;
alias_map::map map_str;
template
using alias_map = std::map < std::string, T > ;
alias_map map_t;
alias_map map_str;
系不系看着舒服很多啊,顿时神清气爽,在C++11中,允许使用using关键字为一个模板来定义别名,实际上using包含了typedef的所有功能,来看下使用using关键字和typedef关键字定义普通类型别名的用法。
typedef unsigned int uint_t;
using uint_t = unsigned int;
typedef std::map map_t;
using map_t = std::map < std::string, int > ;
可以看到在对普通类型的别名定义上,两种方法的使用基本等效,唯一不同的仅仅是定义的语法,using使用起来就像是赋值,但是在定义函数函数指针的时候,using看起来可读性要稍微好一点,比如:
typedef void(*func)(int, int);
using func = void(*)(int, int);
可能突然看起来使用using的方式来定义一个函数指针有点怪,但是习惯了之后会发现使用using这种赋值的方式更适用开发人员的思考方式。下面再显示一个通过typedef和using方式分别来定义一个函数模板的例子:
template
struct FuncSt
{
typedef void(*func)(T, T);
};
FuncSt::func func_typedef;
template
using func_using = void(*func)(T, T);
func_using func_using;
可以看到通过using定义模板别名的语法,仅仅是在普通类型别名语法基础上增加了template参数列表,通过using可以轻松的创建一个模板的别名,而不需要像C++98/03那样增加一个包裹类。但是需要额外注意的是使用using或者typedef仅仅是定义一个别名,不会创造新类型。
在C++98/03里,类模板是支持默认的模板参数的,比如:
template
struct Foo
{
//
};
但是在C++98/03中确实不能支持函数模板的默认模板参数:
template //error in C++98/03 default template arguments
void func(void)
{
//...
}
现在这个限制在C++11中已经解除,上述的定义在C++11中可以直接使用了。在函数模板中当所有模板参数都有默认参数时,函数的调用就如同普通的函数调用,但是对于类末班而言,哪怕所有模板参数都有默认构造函数在使用时还是必须在模板名后跟随<>来实例化。
C++11中函数的默认模板参数在使用规则上和其他的默认参数也有一些区别,普通函数的默认参数必须写在参数列表的最后,而函数的模板参数就没有这个限制,因此当使用默认模板参数和模板参数自动推导时就显示十分灵活,可以指定函数中的一部分参数是默认参数,另一部分采用自动推导。比如:
template
R func(U val)
{
//...
}
int _tmain(int argc, _TCHAR* argv[])
{
func(123);
return 0;
}
但是如果在使用函数模板时如果显示指定模板的参数,由于模板参数的填充顺序是自左向右的,因此像下面这样的调用返回的类型是long类型:
func(123); //func返回类型是填充类型long
模板别名以及默认模板参数是在泛型编程中的一些小细节,是C++11对C++98/03一些细节上的提升,因此介绍的篇幅不多,主要是在使用的时候若可以的话可以通过这些小技巧增加代码可读性,减少代码冗余。