11 / 80 · C++11 · 约 7 分钟
typedef 与 using:类型别名不是新类型
typedef 和 using 都为已有类型提供名字,不创建独立类型,也不是宏替换。using 的语法更适合复杂类型和别名模板;给指针别名添加 const 会限定整个指针类型,而不是自动限定它指向的对象。
改名字,不改类型身份
typedef unsigned long Count; 与 using Count = unsigned long; 在类型别名语义上相同。Count 与 unsigned long 是同一个类型,因此不能分别定义两个仅在这两个参数名义类型上不同的重载。编译器不会因别名叫 UserId 就阻止你传入 OrderId。
如果需要业务层面的区分,应定义包装类或适当的枚举类,让类型系统看到真正不同的类型。别名适合缩短复杂声明、公开库接口中的类型名,或者隔离某些实现选择,但不能承担单位安全和身份隔离的职责。
别名按整体参与限定
using Pointer = int*; 后的 const Pointer 是 const 指针,即 int* const,而不是 const int*。这是类型组合,不是把源代码中的 Pointer 文本替换为 int* 后重新解析。示例用类型特征确认这件事,并合法修改所指的非 const 整数。
类似地,Pointer a, b; 中 a 和 b 都是指针;宏 #define POINTER int* 展开后的 POINTER a, b; 则只有 a 是指针。宏参与的是声明记号拼接,类型别名参与的是真正的类型系统,混淆两者会制造难以发现的声明差异。
using 让泛型表达更直接
函数指针可以写成 using Handler = int (*)(int);,名字在左边,完整类型在右边,比把名字嵌入 typedef 声明符更容易阅读。C++11 还允许 template<class T> using Sequence = std::vector<T>;,把一族类型命名为别名模板。
别名模板本身不能像类模板那样直接偏特化;需要条件选择时,可以借助可特化的类模板或类型工具。也要区分 using Alias = T、using std::swap 与 using namespace std,它们分别是类型别名、引入声明和命名空间指令,并非同一种机制。
容易答错的地方
- 更改公共别名的底层类型可能改变重载选择、对象布局和 ABI;别名不是兼容性屏障。
- 用两个 int 别名分别表示米和秒,仍然允许相互赋值;要拒绝混用必须定义不同类型。
运行一个例子
最低标准 C++11 · 完整程序 · 下载 .cpp
#include <cassert>
#include <iostream>
#include <type_traits>
#include <vector>
using Pointer = int*;
template<class T> using Sequence = std::vector<T>;
int main() {
int value = 1;
const Pointer pointer = &value;
static_assert(std::is_same<decltype(pointer), int* const>::value, "const pointer");
*pointer = 8;
Sequence<int> values{value, 2};
assert(value == 8);
assert(values.size() == 2);
std::cout << values[0] << ' ' << values.size() << '\n';
}
在本地编译
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread basics-typedef-using.cpp -o example && ./example预期结果
8 2
CHECK YOUR UNDERSTANDING
合上答案,试着解释。
using UserId = int; using OrderId = int; 能否通过 void find(UserId) 和 void find(OrderId) 建立两个重载?给出最小改进。
查看参考答案
不能,两次声明拥有完全相同的参数类型。可定义 struct UserId { int value; }; 和 struct OrderId { int value; };,此时它们是两个类类型,两个重载才能区分。根据接口需要,再给包装类型添加 explicit 构造函数和范围校验,而不是期待别名提供隔离。
继续查证
标准草案链接会随工作草案更新;本文版本标记对应示例最低要求,不表示草案中的所有新规则都适用于旧标准。