在 C++ 中,使用复杂类型(如 STL 容器和智能指针)时,类型别名可以简化代码。C++98 使用
typedef
来创建类型别名,而 C++11 引入了更现代的别名声明 (using
)。简单来说,在定义类型别名的时候:using 和 typedef的最主要区别是,
using
可以定义 模板类的别名,但是typedef
不可以。
简单类型别名
对于简单的类型别名,两者用法类似:
typedef int Integer; // 使用 typedef
using Integer = int; // 使用 using
在这种情况下,两者没有显著差异。
复杂类型别名
对于模板类型别名,typedef
的局限性就显现出来了,而 using
提供了更好的解决方案:
使用 typedef
定义模板类型别名
template <typename T>
typedef std::vector<T> Vec_Typedef; // 错误,typedef 不能直接用于模板
Vec_Typedef<int> my_vec;
使用 using
定义模板类型别名
template <typename T>
using Vec_Using = std::vector<T>; // 正确,using 可以直接用于模板
Vec_Using<int> my_vec;
⚠️⚠️⚠️
注意📢:typedef 不可以定义模板类型的别名;但是对于模板类的类型参数
已经推演过或者确定
的类,是可以使用 typedef 定义别名,比如:
typedef std::vector<int> Vec_Int; // 正确,vector 已经指定模板参数类型为 int 。
可读性和可维护性
使用 using
语法定义类型别名时,代码更加直观和可读:
typedef void (*FunctionPointer)(int, int); // 使用 typedef 定义函数指针类型别名
using FunctionPointer = void(*)(int, int); // 使用 using 定义函数指针类型别名
结合 std::enable_if 使用
在模板元编程中,using
使得代码更加简洁。例如,结合 std::enable_if
使用时:
// 使用 typedef
template <typename T>
struct IsInteger {
typedef typename std::enable_if<std::is_integral<T>::value, T>::type type;
};
// 使用 using
template <typename T>
using IsInteger = typename std::enable_if<std::is_integral<T>::value, T>::type;
作用域🌳
using 和 typedef 是有作用域的::
- 定义在所有函数和类的外部,则从定义处到文件结尾。
- 定义在类或者函数内部,则其作用域类似于局部变量的作用范围。
- 另外⚠️,同一作用域内部不可以
重复声明
相同的类型别名!
void foo(bool useVec) {
using DTYPE = float;
// using DTYPE = int; // 错误!在同一作用域内重复声明名字相同的类型。
if (useVec) {
using ArrayType = std::vector<DTYPE>;
} else {
using ArrayType = std::array<DTYPE, 10>; // 正确✅;定义在不同的花括号内,分属不同的作用域,不会引发重定义错误
}
}
using 与 typedef 的区别和优点
using 相较于 typedef 有许多优点,尤其是在现代 C++ 编程中更为常用。下面我们通过几个具体的例子和原因来展开对比讲解。
1. 模板别名
using
允许我们定义模板别名,而 typedef
则不行。这在处理模板类型时尤为重要,因为模板在泛型编程中广泛使用。
示例:
template <typename T>
using Vec = std::vector<T>; // 使用 using 定义模板别名
使用 typedef
定义模板别名会变得非常麻烦甚至不可能,因为 typedef
不能直接处理模板参数 (但是对于模板参数类型已经推演过或者指定过
的模板类是可以使用 typedef 定义别名的 。相反,using
简单直观地允许我们定义模板别名,使代码更简洁明了。
2. 可读性
using
语法更加直观,易于理解和维护。相比之下,typedef
在某些复杂的类型声明中可读性较差。
示例:
using FunctionPointer = void(*)(int, int); // 简单明了的函数指针别名
相同的类型别名如果使用 typedef
来定义,看起来会稍显繁琐:
typedef void(*FunctionPointer)(int, int); // 使用 typedef 定义函数指针别名
可以看出,using
语法更加接近自然语言描述,直观易懂。
3. 模板元编程
在复杂的模板元编程中,using
可以显著减少代码的复杂性,使代码更易读。模板元编程涉及到很多模板参数和类型推导,使用 using
可以更清晰地表达类型关系。
示例:
template <typename T>
using IsInteger = typename std::enable_if<std::is_integral<T>::value, T>::type;
这段代码使用 using
定义了一个模板别名 IsInteger
,用于启用整数类型。相比之下,使用 typedef
来实现相同的功能会显得非常复杂,难以理解:
template <typename T>
struct IsInteger {
typedef typename std::enable_if<std::is_integral<T>::value, T>::type type;
};
using
使得模板元编程中的类型定义变得更加简洁明了,从而提高代码的可读性和可维护性。
using 可以完全取代 typedef 吗?
using
可以在大多数情况下取代 typedef
,特别是在现代 C++ 编程中。然而,typedef
仍有一些特定的用例,在这些用例中,typedef
更为合适。
typedef
可以用来定义匿名联合体或结构体,这在某些特定情况下是必要的。
示例:
typedef struct {
int x;
int y;
} Point;
使用 using
无法直接定义匿名联合体或结构体。
虽然 using
在大多数情况下可以取代 typedef
,并且在现代 C++ 编程中有许多优点,但 typedef
在定义匿名类型和维护旧代码方面仍有其不可替代的作用。因此,推荐在新代码和现代 C++ 项目中尽量使用 using
,但在需要定义匿名类型或兼容旧代码时仍可以使用 typedef
。
总结
typedef
不支持模板化,但别名声明(using)支持。使用别名模板可以避免使用 ::type 后缀和在模板中频繁使用 typename 前缀来引用 typedef。C++14 为所有 C++11 类型特性转换提供了别名模板的支持。