関数オーバーロード
同じ名前で異なる処理。C++の多態性を実現
概要
関数オーバーロードは、 同じ名前で複数の関数を定義することができる機能です。
引数の型や個数が異なれば、 呼び出すときに自動的に正しい関数が選ばれます。
基本例
例:add 関数の複数バージョン
#include <iostream>
// int版
int add(int a, int b) {
return a + b;
}
// double版
double add(double a, double b) {
return a + b;
}
// 3つの数を足す版(int)
int add(int a, int b, int c) {
return a + b + c;
}
int main() {
std::cout << add(2, 3) << std::endl; // 5(int版)
std::cout << add(2.5, 3.5) << std::endl; // 6(double版)
std::cout << add(2, 3, 4) << std::endl; // 9(3つの数版)
return 0;
}
5
6
9
同じ名前addでも、
引数の型と個数を見て適切な関数が呼ばれます。
オーバーロードの条件
オーバーロードが成立するには、関数のシグニチャが異なる必要があります。
| 条件 | OK? | 理由 |
|---|---|---|
add(int, int) と add(double, double) |
OK | 引数の型が異なる |
add(int, int) と add(int, int, int) |
OK | 引数の個数が異なる |
add(int, double) と add(double, int) |
OK | 引数の順序が異なる(型が異なる) |
int add(int, int) と double add(int, int) |
NG | 戻り値の型だけが異なる(呼び出し時に判断できない) |
実践例
例1: 最大値を求める
#include <iostream>
// 2つの int の最大値
int maximum(int a, int b) {
return (a > b) ? a : b;
}
// 2つの double の最大値
double maximum(double a, double b) {
return (a > b) ? a : b;
}
// 3つの int の最大値
int maximum(int a, int b, int c) {
int maxAB = maximum(a, b);
return (maxAB > c) ? maxAB : c;
}
int main() {
std::cout << maximum(10, 20) << std::endl; // 20
std::cout << maximum(3.5, 2.5) << std::endl; // 3.5
std::cout << maximum(5, 10, 3) << std::endl; // 10
return 0;
}
20
3.5
10
例2: 属性値を設定する
#include <iostream>
#include <string>
class Person {
public:
std::string name;
int age;
double height;
void setInfo(std::string n) {
name = n;
}
void setInfo(int a) {
age = a;
}
void setInfo(double h) {
height = h;
}
};
int main() {
Person p;
p.setInfo("太郎"); // 名前を設定
p.setInfo(25); // 年齢を設定(int)
p.setInfo(175.5); // 身長を設定(double)
std::cout << p.name << ", " << p.age << ", " << p.height << std::endl;
return 0;
}
太郎, 25, 175.5
参照を使ったオーバーロード
例:const参照 vs 非const参照
#include <iostream>
void printValue(const int & x) {
std::cout << "const版: " << x << std::endl;
}
void printValue(int & x) {
std::cout << "非const版: " << x << std::endl;
}
int main() {
int num = 42;
printValue(num); // 非const版を呼び出す
const int const_num = 42;
printValue(const_num); // const版を呼び出す
return 0;
}
非const版: 42
const版: 42
型変換による呼び出し
完全一致がない場合、 コンパイラは型変換を試みます。
#include <iostream>
void display(int x) {
std::cout << "int版: " << x << std::endl;
}
void display(double x) {
std::cout << "double版: " << x << std::endl;
}
int main() {
display(5); // int版(完全一致)
display(5.5); // double版(完全一致)
display(5L); // int版(long から int へ変換)
return 0;
}
実践例:計算機関数
#include <iostream>
int power(int base, int exponent) {
int result = 1;
for (int i = 0; i < exponent; i++) {
result *= base;
}
return result;
}
double power(double base, int exponent) {
double result = 1.0;
for (int i = 0; i < exponent; i++) {
result *= base;
}
return result;
}
int main() {
std::cout << "2^3 = " << power(2, 3) << std::endl; // 8
std::cout << "2.5^3 = " << power(2.5, 3) << std::endl; // 15.625
return 0;
}
2^3 = 8
2.5^3 = 15.625
オーバーロード解析のルール
関数が呼び出された時、以下の順序で適切な関数が選ばれます:
- 完全一致:引数と戻り値の型が完全に一致
- 標準変換:int → long、bool → int など
- ユーザー定義変換:クラスのコンストラクタなど
複数の関数が同じ優先順位で候補になった場合は エラー(曖昧)になります。
ポイント
- オーバーロードで同じ名前で複数の処理を実装
- 引数の型と個数が異なる必要がある
- 戻り値の型だけでは区別されない
- 呼び出し時に自動的に正しい関数が選ばれる
- 型変換が行われることに注意
よくある誤り
誤り1: 戻り値の型だけが異なる
int getValue() { return 42; }
double getValue() { return 42.0; } // エラー!: 呼び出し時に判断できない
誤り2: 曖昧なオーバーロード
void process(int x) { }
void process(long x) { } // 曖昧
// process(42) がどちらか判断できない場合がある
誤り3: namespace や class の境界を越えた呼び出し
namespace ns1 {
void func(int x) { }
}
namespace ns2 {
void func(double x) { }
}
int main() {
func(42); // どちらが呼ばれる?(曖昧)
ns1::func(42); // これで明確
}
やってみよう
練習1: 面積を計算する関数。正方形、円、三角形に対応。
練習2: 入力を受け取る関数。int と string の2バージョン。
練習3: 複数の add 関数。2つ、3つ、4つの数を足す。
チャレンジ: abs 関数の複数バージョン(int, double, long)。
まとめ
- 関数オーバーロードで同じ操作に対して型別の実装
- 引数の型と個数で関数を区別
- 戻り値の型のみ異なる場合はオーバーロード不可
- 適切に使うと可読性が向上し、使いやすいAPI