现代C++中的using
与typedef
原文:https://www.internalpointers.com/post/differences-between-using-and-typedef-modern-c
译者:0xAA55
清爽的类型别名定义方式
为何使用类型别名
从初学C语言的时候,我们就已经掌握了通过给变量类型起别名的方式来避免代码变得过于复杂或者意义不明。简而言之,你可以给一个现有的变量类型(比如int
)取一个新的名字(比如Pixel
)。类型别名可用于帮助你书写清爽、干净且易于阅读的代码。
假设你正在弄一个图形库,请观察以下两个函数的差异。
int getScreenWidth();
或者:
Pixel getScreenWidth();
后者显然更直观,在声明了别名Pixel
后,它返回的数值是干什么的就一目了然了。
另一个例子如下:
std::map<std::string, std::vector<std::string>> map;
或者:
Map map;
需要注意使用类型别名并不意味着创建新的类型。它只是以同义词的方式来指代原有的类型名。上文的Pixel
依然是个int
,且Map
依然是鬼畜的std::map<std::string, std::vector<std::string>>
,可以被用在只接受int
或者std::map<std::string, std::vector<std::string>>
类型的参数的函数调用上(而并不需要函数的参数非得是Pixel
或者Map
)。
声明新别名的方式
在现代C++里有两种方式声明类型别名,第一种是传统的typedef
关键字:
typedef [原有类型] [你要的别名];
例如:
typedef int Pixel;
typedef std::map<std::string, std::vector<std::string>> Map;
而另一种声明别名的方式则是通过使用从C++11开始引入的using
关键字来实现的:
using [你要的别名] = [原有类型];
例如:
using Pixel = int;
using Map = std::map<std::string, std::vector<std::string>>;
效果上,这两种方式是相同的。最终你都可以使用Pixel
和Map
这两个你定义的新名称,想怎么用就怎么用。但……
using
最适合模板
上文的Map
的定义(不管是用typedef
还是using
)它本来的类型已经被定死了:永远只会是一个std::map<std::string, std::vector<std::string>>
,改不了的,比如你想用一个类型为std::map<int, std::vector<int>>
的Map
,你就得重新用另一个别名了。
但C++11的using
其实能够创建“模板别名”:可以给类型模板起别名。它打开了一扇门,使你能指定原有类型里的模板参数。
定义的方式如下:
template<[模板参数列表]> using [你要的别名] = [原有类型];
例如:
template<typename T1, typename T2> using Map = std::map<T1, std::vector<T2>>;
然后我就可以定义不同类型的Map
变量了:
// 实际类型:std::map<std::string, std::vector<std::string>> (和上文例子里相同)
Map<std::string, std::string> map1;
// 实际类型:std::map<int, std::vector<int>>
Map<int, int> map2;
// 实际类型:std::map<int, std::vector<float>>
Map<int, float> map3;
虽然其实使用传统的typedef
也可以实现相同的效果,但是非常难设计、复杂棘手。不值得弄。
模板声明不能被放在代码块里
你可以把类型别名定义放在任何位置,不管是用typedef
还是using
,你都可以放在任意地方:命名空间里、类里,或者块里({}
里面)
但模板别名则和别的C++模板一样遵循一个规则:它们不能被放在块里,因为它们最终都是模板定义,所以都是放在命名空间里,或者不使用命名空间的时候放全局位置。
参考来源
cprogramming.com - The Typedef Keyword in C and C++
cppreference.com - Type alias, alias template
cppreference.com - typedef specifier
StackOverflow - What is the difference between 'typedef' and 'using' in C++11?