はじめに
C++では、同じスコープ内に、同じ名前の関数や演算子を複数定義することができます。これを**オーバーロード(多重定義)**と呼びます。コンパイラは、渡された引数の型や個数(シグネチャ)を元に、どのバージョンを呼び出すべきかを自動的に判断してくれます。
オーバーロードは、オブジェクト指向の**多態性(ポリモーフィズム)**を静的に実現する仕組みであり、コードの直感性と可読性を大きく向上させます。
この記事では、関数オーバーロードと、自作クラスを組み込み型のように扱えるようにする演算子オーバーロードの基本を網羅的に解説します。
1. 関数オーバーロード
同じ名前で、引数の型や個数が異なる関数を複数定義することです。
#include <iostream>
// int型を2倍にする
int double_value(int n) {
return n * 2;
}
// double型を2倍にする
double double_value(double n) {
return n * 2.0;
}
int main() {
std::cout << double_value(10) << std::endl; // int版が呼ばれる
std::cout << double_value(5.5) << std::endl; // double版が呼ばれる
return 0;
}
2. 算術演算子のオーバーロード
+
, -
, *
, /
, +=
などの算術演算子を、自作のクラスで使えるように定義します。
サンプルコード
#include <iostream>
struct Vector2D {
int x, y;
// メンバ関数として += をオーバーロード
Vector2D& operator+=(const Vector2D& rhs) {
this->x += rhs.x;
this->y += rhs.y;
return *this;
}
};
// グローバル関数として + をオーバーロード
Vector2D operator+(Vector2D lhs, const Vector2D& rhs) {
lhs += rhs; // 実装は += を再利用するのが定石
return lhs;
}
int main() {
Vector2D v1{1, 2}, v2{3, 4};
Vector2D v3 = v1 + v2; // operator+ が呼ばれる
std::cout << "v3: (" << v3.x << ", " << v3.y << ")" << std::endl; // -> (4, 6)
return 0;
}
3. 比較演算子のオーバーロード
==
, !=
, <
, >
などをオーバーロードすることで、自作クラスのオブジェクト同士を比較できるようになります。
サンプルコード (C++20以前)
struct Product {
int id;
};
// == と < を定義すれば、他はこれらを元に実装できる
bool operator==(const Product& a, const Product& b) { return a.id == b.id; }
bool operator<(const Product& a, const Product& b) { return a.id < b.id; }
bool operator!=(const Product& a, const Product& b) { return !(a == b); }
bool operator>(const Product& a, const Product& b) { return b < a; }
// ... <=, >= も同様
宇宙船演算子 <=>
(C++20)
C++20では、宇宙船演算子 (<=>
) を一つ定義するだけで、コンパイラが他の全ての比較演算子 (==
, !=
, <
, >
, <=
, >=
) を自動的に生成してくれます。
#include <compare> // three-way comparison
struct Product {
int id;
// <=>をデフォルト実装するだけで、全ての比較演算子が利用可能になる
auto operator<=>(const Product& other) const = default;
};
int main() {
Product p1{10}, p2{20};
if (p1 < p2) { /* 比較可能 */ }
return 0;
}
4. その他の便利な演算子オーバーロード
添字演算子 []
オブジェクトを配列のように扱えるようにします。
#include <vector>
class MyData {
public:
std::vector<int> data = {10, 20, 30};
// 戻り値を参照(&)にすると、値の書き換えも可能になる
int& operator[](int index) {
return data[index];
}
};
int main() {
MyData d;
std::cout << d[1] << std::endl; // -> 20
d[1] = 99; // 値を書き換える
std::cout << d[1] << std::endl; // -> 99
return 0;
}
関数呼び出し演算子 ()
オブジェクトを関数のように呼び出せるようにします(関数オブジェクトまたはファンクタ)。
C++
#include <iostream>
struct Adder {
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Adder add;
int result = add(3, 4); // オブジェクトを関数として呼び出す
std::cout << result << std::endl; // -> 7
return 0;
}
まとめ
今回は、C++のオーバーロード機能について、関数と各種演算子を例に解説しました。演算子オーバーロードを適切に使うことで、自作したクラスを、int
などの組み込み型と全く同じような、直感的で自然な構文で扱えるようになり、コードの可読性を劇的に向上させることができます。