普通类型

using 的别名语法覆盖了 typedef 的全部功能,将这两种语法对比一下:

1
2
3
4
5
6
7
// 重定义unsigned int
typedef unsigned int uint_t;
using uint_t = unsigned int;

// 重定义std::map
typedef std::map<std::string, int> map_int_t;
using map_int_t = std::map<std::string, int>;

可以看到,在重定义普通类型上,两种使用方法的效果是等价的,唯一不同的是定义语法。

函数指针

typedef 的定义方法和变量的声明类似:像声明一个变量一样,声明一个重定义类型,之后在声明之前加上 typedef 即可。这种写法凸显了 C/C++ 中的语法一致性,但有时却会增加代码的阅读难度。比如重定义一个函数指针时:

1
typedef void (*func_t)(int, int);

与之相比,using 后面总是立即跟随新标识符(Identifier),之后使用类似赋值的语法,把现有的类型(type-id)赋给新类型:

1
using func_t = void (*)(int, int);

从上面的对比中可以发现,C++11 的 using 别名语法比 typedef 更加清晰。因为 typedef 的别名语法本质上类似一种解方程的思路。而 using 语法通过赋值来定义别名,和我们平时的思考方式一致。

1
2
3
4
5
6
7
/* C++98/03 */
template <typename T>
struct func_t
{
typedef void (*type)(T, T);
};
func_t<int>::type xx_1;
1
2
3
4
5
/* C++11 */
template <typename T>
using func_t = void (*)(T, T);

func_t<int> xx_2;

需要注意的是,using 语法和 typedef 一样,并不会创造新的类型。也就是说,上面示例中 C++11 的 using 写法只是 typedef 的等价物。虽然 using 重定义的 func_t 是一个模板,但 func_t<int> 定义的 xx_2 并不是一个由类模板实例化后的类,而是 void(*)(int, int) 的别名。

模板别名

以一个固定以 std::string 为 key、映射到 int 或另一个 std::string 的 map 为例,使用 typedef 需要一个虽然简单但却略显烦琐的 str_map 外敷类。

1
2
3
4
5
6
template <typename Val>
struct str_map
{
typedef std::map<std::string, Val> type;
};
str_map<int>::type map1;

使用 using 定义模板别名:

1
2
3
template <typename Val>
using str_map_t = std::map<std::string, Val>;
str_map_t<int> map1;

通过 using 定义模板别名的语法,只是在普通类型别名语法的基础上增加 template 的参数列表。使用 using 可以轻松地创建一个新的模板别名,而不需要像 C++98/03 那样使用烦琐的外敷模板。