運算子多載讓使用者定義型別 (類別) 能像內建型別一樣自然運作:a + b、v[i]、++iter。核心目標是降低學習曲線、減少錯誤、提升可讀性。本文涵蓋語法規則、可/不可多載運算子、實作模式、決策準則與現代 C++ 演進。
| 形式 | 宣告範例 | 適用運算子 |
|---|---|---|
| 成員函數 | Type operator@(Arg) | 一元運算子、= [] () ->* 必須為成員 |
| 友元/非成員函數 | Type operator@(Lhs, Rhs) | 二元運算子、左運算元非類別型別時 |
class Complex {
double re, im;
public:
Complex& operator+=(const Complex& rhs) { // 複合指派回傳 *this
re += rhs.re; im += rhs.im;
return *this;
}
Complex operator-() const { return Complex(-re, -im); } // 一元負號
Complex& operator++() { ++re; return *this; } // 前置++
Complex operator++(int) { Complex tmp=*this; ++*this; return tmp; } // 後置++
};
class Complex { ... };
// 非成員,需友元存取私有成員
Complex operator+(Complex lhs, const Complex& rhs) { // lhs 以值傳遞支援移動優化
lhs += rhs;
return lhs; // NRVO/移動語意優化
}
bool operator==(const Complex& lhs, const Complex& rhs) {
return lhs.re == rhs.re && lhs.im == rhs.im;
}
| 可多載 (大多數) | 不可多載 |
|---|---|
| 算術:+ - * / % | . (成員存取) |
| 位元:& | ^ ~ << >> | ?: (三元條件) |
| 比較:== != < > <= >= | :: (範圍解析) |
| 邏輯:&& || ! | .* (成員指標存取) |
| 指派:= += -= *= /= %= &= |= ^= <<= >>= | sizeof (技術上屬運算子) |
| 其他:[] () -> ->* new delete , (逗號) | typeid, alignof, co_await 等 |
operator&&/operator|| 失去短路求值特性,極不建議多載operator, (逗號) 失去排序點保證,極不建議多載operator& (取址) 多載會干擾標準庫容器,**極不建議**class Vec {
double x, y;
public:
Vec& operator+=(const Vec& rhs) { x+=rhs.x; y+=rhs.y; return *this; }
friend Vec operator+(Vec lhs, const Vec& rhs) { lhs += rhs; return lhs; } // 值傳遞 lhs
// -, *, / 同理
};
class Matrix {
std::vector data;
size_t cols;
public:
double& operator()(size_t r, size_t c) { return data[r*cols + c]; }
const double& operator()(size_t r, size_t c) const { return data[r*cols + c]; }
// 支援 m(1,2) = 3.14;
}
template<typename T>
class UniquePtr {
T* ptr;
public:
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
// 支援 *p, p->member
};
std::ostream& operator<<(std::ostream& os, const MyClass& obj) {
return os << "MyClass{val=" << obj.val << "}";
}
operator% 用於字串格式化 (Python 風格) 在 C++ 易誤解operator+ 執行非交換操作operator&&、operator||、operator,、operator&| 陷阱 | 正確做法 |
|---|---|
回傳值而非參考 (T& operator+=) | 複合指派回傳 *this 參考 |
| 前置/後置 ++ 區分錯誤 | 前置 T& operator++(),後置 T operator++(int) |
operator[] 僅提供非 const 版 | 提供 const/非 const 雙版本 |
operator== 非對稱/非傳遞 | 嚴格滿足等價關係 (自反、對稱、傳遞) |
| 隱式轉換導致意外多載解析 | 關鍵建構子加 explicit |
operator+(T lhs, const T& rhs) 值傳遞 lhs 啟用移動優化operator<=> (三向比較) 自動產生 == != < <= > >=template<Addable T> T operator+(T, T)const char* 隱式轉換auto [x, y] = point; 依賴 get<0>() 或公有成員operator[] 回傳參考?class Array {
int data[100];
public:
int& operator[](size_t i) { return data[i]; } // 左值:a[3] = 5;
int operator[](size_t i) const { return data[i]; } // 右值:x = a[3];
};
回傳參考才能支援左值語意 (可被指派),回傳值僅支援右值讀取。
運算子多載是「為使用者設計,非為實作者設計」。好的多載讓程式碼讀起來像領域語言 (DSL),壞的多載製造驚喜與 Bug。遵循慣例、保持語意直覺、善用現代特性 (移動語意、<=>、Concepts),讓類別自然融入 C++ 生態系。