函式多載允許在同一作用域內定義多個同名但參數列不同的函式。編譯器依據呼叫時的實際引數型別、個數、順序,自動選擇最匹配的版本。這是 C++ 支援多型的基石之一(靜態多型/編譯期多型)。
適用場景:對不同型別執行「同一語意操作」。
// 經典範例:取最大值
int Max(int a, int b);
double Max(double a, double b);
int Max(const std::vector& v);
double Max(const std::vector& v);
呼叫端無需記憶 MaxInt、MaxDouble、MaxVector 等不同名稱,認知負擔大幅降低。
不適用場景:操作語意不同,僅因「名稱相似」強行多載。
// 反例:游標移動
Screen& MoveUp(); // 向上
Screen& MoveDown(); // 向下
Screen& MoveLeft(); // 向左
Screen& MoveRight(); // 向右
// 若強行多載為 Screen& Move(); 呼叫端完全無法理解意圖
原則:多載應服務於「同一概念操作在不同型別上的實現」,而非單純減少名稱數量。
當呼叫 foo(2.4) 面對多個 foo 多載版本時,編譯器執行:
同名且可見的所有函式。例如:
void S(); // #1
void S(int); // #2
void S(double, double = 1.2); // #3
void S(const char*, const char*); // #4
void Max(int, int); // #5 (名稱不同,排除)
候選集 = {#1, #2, #3, #4}
條件:
對 S(2.4):
double → int 可轉換 → 可行double → double 精確匹配,第 2 參數有預設值 → 可行double → const char* 無標準轉換 → 排除可行集 = {#2, #3}
依據轉換等級由優到劣排名:
const/volatile 調整、陣列/函式退化為指標、T → T& 綁定參考。bool → int, char → int, short → int, float → double 等不失精度的標準轉換。void*、衍生類別指標轉基底類別指標等。... 參數。回到範例:
double → int (標準轉換,等級 3)double → double (精確匹配,等級 1)結論:呼叫 #3 S(double, double)。
void Func(int a, int b = 10);
void Func(double a); // 呼叫 Func(5) 會產生模糊:#1 精確匹配 vs #2 標準轉換
建議:避免多載與預設參數混用,或以明確轉型消除歧義。
void Process(int&); // 綁定 lvalue
void Process(const int&); // 綁定 rvalue 或 const lvalue
void Process(int&&); // C++11 移動語意,綁定 rvalue
善用此特性實作完美轉發與移動語意。
非模板函式優於模板實例化;模板偏特化優於通用模板。可用於提供預設實作並針對特定型別優化。
引數所在命名空間的同名函式也會納入候選集,這是運算子多載與標準庫擴充的關鍵機制。
呼叫 foo(args)
│
▼
候選集: 同名且可見的所有 foo
│
▼
可行集: 參數數匹配(含預設值) 且 每個實引數可隱含轉換
│
▼
最佳可行: 依轉換等級排名,唯一最優者勝出
│
├── 唯一最佳 → 編譯通過
├── 無可行 → 錯誤: no matching function
└── 多個同級最佳 → 錯誤: ambiguous call
函式多載是 C++ 介面設計的核心利器。理解編譯器的三階段決策機制,能幫助你設計出直覺、安全、無歧義的 API。記住:多載服務於語意一致性,而非命名節省。下次設計多載集合時,先問自己:「這些函式在概念上真的是同一件事嗎?」