第 18.3 節
using
0瀏覽次數0訪問次數--跳出率--平均停留
本節解決什麼問題
C 語言中,用 typedef 給類型起別名。但 typedef 的語法很彆扭,特別是給函數指針、模板起別名時,寫法不直觀、難讀。
C++11 引入了 using 別名聲明,語法更清晰,還支持模板別名(typedef 做不到)。
這個特性是什麼
using 有兩種常見用途:
- 類型別名:給已有類型起新名字,類似
typedef但語法更清晰。 - 模板別名:給模板類型起別名(
typedef做不到這一點)。
C++ 標準版本
C++11(類型別名語法),C++11 模板別名。
需要的頭文件
不需要額外頭文件。using 是語言關鍵字。
基本語法
// 类型别名
using 新名字 = 已有类型;
// 模板别名
template<typename T>
using 新名字 = 模板类型<T...>;
與 typedef 的對比
| 場景 | typedef | using |
|---|---|---|
| 基本類型 | typedef int Integer; | using Integer = int; |
| 函數指針 | typedef void (*Func)(int); | using Func = void(*)(int); |
| 數組 | typedef int Arr10[10]; | using Arr10 = int[10]; |
| 模板別名 | ❌ 不支持 | using Vec<T> = std::vector<T>; |
using 能做什麼,不能做什麼
using 創建的是別名,不是新類型。它能讓複雜類型更好讀,但不能阻止你把兩個底層相同的類型混用。
| 需求 | using 是否適合 | 說明 |
|---|---|---|
| 縮短複雜類型名 | ✅ 適合 | using Callback = std::function<void(int)>; |
| 統一項目裏的業務名稱 | ✅ 適合 | using UserId = int; 讓代碼更有語義 |
| 定義模板的簡化寫法 | ✅ 很適合 | template<typename T> using Vec = std::vector<T>; |
| 防止不同業務類型互相傳錯 | ❌ 不夠 | using Meter = double; 和 using Second = double; 本質仍都是 double |
| 創建真正的新類型 | 用 struct / class / enum class | 編譯器才能幫你檢查混用 |
示例代碼
示例 1:using 基礎——替換 typedef
#include <iostream>
#include <string>
#include <vector>
// 旧写法(typedef)
typedef std::vector<int> IntVector1;
// 新写法(using)
using IntVector2 = std::vector<int>;
int main()
{
// 两种写法效果完全一样
IntVector1 v1 = {1, 2, 3};
IntVector2 v2 = {4, 5, 6};
std::cout << "v1: ";
for (int n : v1) std::cout << n << " ";
std::cout << "\n";
std::cout << "v2: ";
for (int n : v2) std::cout << n << " ";
std::cout << "\n";
return 0;
}
運行結果:
v1: 1 2 3
v2: 4 5 6
示例 2:在示例 1 基礎上,using 給函數指針起別名
#include <iostream>
// typedef 的函数指针写法(别扭)
typedef int (*FuncPtr1)(int, int);
// using 的函数指针写法(直观)
using FuncPtr2 = int (*)(int, int);
int add(int a, int b)
{
return a + b;
}
int multiply(int a, int b)
{
return a * b;
}
int main()
{
FuncPtr2 op1 = add;
FuncPtr2 op2 = multiply;
std::cout << "add(3, 5) = " << op1(3, 5) << "\n";
std::cout << "multiply(3, 5) = " << op2(3, 5) << "\n";
return 0;
}
運行結果:
add(3, 5) = 8
multiply(3, 5) = 15
示例 3:在示例 2 基礎上,模板別名(typedef 做不到)
#include <iostream>
#include <vector>
#include <map>
#include <string>
// 模板别名:定义一个泛型的 vector
template<typename T>
using Vec = std::vector<T>;
// 模板别名:键为 string 的 map
template<typename T>
using StringMap = std::map<std::string, T>;
int main()
{
Vec<int> vi = {1, 2, 3};
Vec<double> vd = {1.1, 2.2, 3.3};
StringMap<int> scores = {{"Alice", 85}, {"Bob", 92}};
std::cout << "Vec<int>: ";
for (int n : vi) std::cout << n << " ";
std::cout << "\n";
std::cout << "Vec<double>: ";
for (double d : vd) std::cout << d << " ";
std::cout << "\n";
std::cout << "StringMap<int>: ";
for (const auto& p : scores)
std::cout << p.first << ":" << p.second << " ";
std::cout << "\n";
return 0;
}
運行結果:
Vec<int>: 1 2 3
Vec<double>: 1.1 2.2 3.3
StringMap<int>: Alice:85 Bob:92
示例 4:在示例 3 基礎上,using 別名不是新類型
#include <iomanip>
#include <iostream>
using Meter = double;
using Second = double;
double speed(Meter distance, Second time)
{
return distance / time;
}
struct MeterValue
{
double value;
};
struct SecondValue
{
double value;
};
double safe_speed(MeterValue distance, SecondValue time)
{
return distance.value / time.value;
}
int main()
{
Meter distance = 100.0;
Second time = 9.58;
std::cout << std::fixed << std::setprecision(2);
std::cout << "speed = " << speed(distance, time) << "\n";
// using 只是别名,下面这行语义错了,但编译器仍然允许
std::cout << "wrong order = " << speed(time, distance) << "\n";
MeterValue d{100.0};
SecondValue t{9.58};
std::cout << "safe speed = " << safe_speed(d, t) << "\n";
// safe_speed(t, d); // ❌ 编译错误:SecondValue 不能当 MeterValue 用
return 0;
}
運行結果:
speed = 10.44
wrong order = 0.10
safe speed = 10.44
運行結果
見上方每個示例的"運行結果"。
示例中的關鍵語法解釋
| 示例 | 講了什麼 | 新出現的語法 | 爲什麼這樣寫 | 注意事項 |
|---|---|---|---|---|
| 示例 1 | 基礎類型別名 | using Name = Type; | 語法更直觀,= 讓人一眼看出"別名 = 原類型" | typedef 也能做,但 using 更清晰 |
| 示例 2 | 函數指針別名 | using Func = int(*)(int,int); | 名字在中間,比 typedef 的名字夾在中間更好讀 | 實際項目中建議用 std::function 代替裸函數指針 |
| 示例 3 | 模板別名 | template<T> using Vec = vector<T>; | typedef 做不到模板別名,這是 using 的獨特能力 | 非常適合簡化嵌套模板類型 |
| 示例 4 | using 不是新類型 | using Meter = double、包裝結構體 | 別名提升可讀性,結構體提供類型安全 | 需要防止參數傳反時,不要只靠 using |
常見錯誤
錯誤 1:把 using 別名的 = 方向寫反
using int = MyInt; // ❌ 方向反了!应该是 using 别名 = 原类型
正確做法:using MyInt = int;
錯誤 2:在頭文件中不加限制地使用 using
// 头文件中
using namespace std; // ❌ 污染全局/外部命名空间!
正確做法:頭文件中避免 using namespace,只在 .cpp 的實現文件或函數內部使用。
錯誤 3:using 聲明和 using 別名混淆
using std::cout; // using 声明:引入名字
using MyVec = std::vector<int>; // using 别名:创建别名
這是兩種不同的用法,前者是引入名字到當前作用域,後者是創建類型別名。
錯誤 4:以爲 using 能提供強類型檢查
using UserId = int;
using ProductId = int;
void load_user(UserId id);
ProductId pid = 10;
load_user(pid); // 能编译,因为 UserId 和 ProductId 本质都是 int
正確做法:如果必須防止混用,用 struct UserId { int value; }; 這種包裝類型,或者用 enum class 表達有限集合。
使用建議
- 用
using代替typedef:語法更清晰,還支持模板別名。 - 善用模板別名簡化代碼:例如
using TaskMap = std::map<int, std::function<void()>>;。 - 不要在頭文件中
using namespace:避免命名空間污染。 - using 聲明(
using std::cout)可以用在 .cpp 或函數內部,簡化代碼。 - 需要強類型時不要只用 using:using 改善可讀性,
struct/class/enum class才能改變類型本身。
小結
using NewName = OldType;比typedef語法更直觀。template<typename T> using Alias = ...;是 typedef 做不到的模板別名。- 函數指針別名、容器類型別名在項目中很實用。
using只是別名,不是新類型;需要類型安全時要引入包裝類型。- 別混淆
using別名和using聲明(命名空間引入)。