【C++】関数のオーバーロードとは?|同じ名前で複数の関数を作る仕組み

C++

今回は、C++でよく出てくる「関数のオーバーロードとは何か」について解説していきます。

「関数のオーバーロードって何?」
「同じ名前の関数を複数作れるの?」
「型ごとに関数名を変えるのが面倒…」

こんな疑問はありませんか?

「int版」「float版」「文字列版」と、中身が似ている関数を型ごとに別名で作るのは面倒ですよね。関数のオーバーロードを使うと、同じ名前のまま複数の型に対応できて、呼ぶ側もスッキリします。

この記事を読み終えると、あなたは関数オーバーロードの意味・書き方・ゲームでの使いどころ・初心者がハマりやすいミスをしっかり理解できると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

関数のオーバーロードとは?

関数のオーバーロードとは、同じ名前で、引数が違う関数を複数定義できる仕組みです。呼び出したときの引数を見て、コンパイラがぴったり合う関数を自動で選んでくれます。

#include <iostream>
#include <string>

// 同じ名前で引数違いの関数を用意する
void Print(int n)                { std::cout << "整数: " << n << "n"; }
void Print(double d)             { std::cout << "小数: " << d << "n"; }
void Print(const std::string& s) { std::cout << "文字列: " << s << "n"; }

int main() {
    Print(10);        // 整数版が呼ばれる
    Print(3.14);      // 小数版
    Print("Hello");   // 文字列版
    return 0;
}
整数: 10
小数: 3.14
文字列: Hello

すべて Print という同じ名前なのに、渡した型に合わせて正しい関数が選ばれています。文字列の扱いはstringとchar配列の違いの記事もどうぞ。

なぜ関数のオーバーロードが必要?

理由は、呼ぶ側が名前を覚えやすく、コードが読みやすくなるからです。PrintInt PrintDouble …と別名を作ると、使うたびに正しい名前を探す手間がかかります。同じ役割は同じ名前にできるのがオーバーロードの強みです。

実践例:ゲームでの使い方

ゲームでは、同じ「生成する」処理でも対象が違うことがあります。オーバーロードなら Spawn という名前で統一できます。

void Spawn(int enemyId)          { /* 敵を生成 */ }
void Spawn(int x, int y)         { /* 座標にアイテム生成 */ }
void Spawn(const std::string& s) { /* 名前でオブジェクト生成 */ }

引数の数や型が違えば、同じ Spawn でも別の関数として使い分けられます。クラス設計の基本はクラスとは?の記事もあわせてどうぞ。

【重要】私が実際に関数オーバーロードで困った体験談

個人開発で void Damage(int)void Damage(float) を作っていたとき、Damage(0) と呼んだら「あいまいな呼び出しです(ambiguous)」というコンパイルエラーが出て固まりました。

…と思いきや今度は別の場面で、Damage(0.0) のつもりが 0 を渡してしまい、意図せず int 版が呼ばれて挙動がズレていたこともありました。原因はどちらも数値の暗黙変換0.0f のように型をはっきり書いて渡すようにしたら、迷わず正しい関数が選ばれるようになりました。

関数オーバーロード使用時のよくある失敗例と対処法

①戻り値の型だけ違うのはNG

int f()double f() のように戻り値だけ違うのはオーバーロードできません。区別は「引数」で行うと覚えましょう。

②暗黙変換で「あいまい」エラー

intdouble の両方があると、どちらにも変換できる値で迷います。型をはっきり書いて渡す0.0f など)と回避できます。

③引数が実質同じでオーバーロードにならない

引数の数も型も同じだと、ただの再定義エラーです。オーバーロードには引数に違いが必要です。

注意点

  • 区別は引数の数・型で行う(戻り値だけでは不可)
  • あいまいさを避けるため、型を明示して渡す
  • 「あらゆる型で同じ処理」をしたいならテンプレートも検討する

まとめ

  • 関数オーバーロードは同じ名前で引数違いの関数を作る仕組み
  • コンパイラが引数を見てぴったりの関数を選ぶ
  • 区別は引数で。戻り値だけでは区別できない
  • 暗黙変換の「あいまい」に注意し、型を明示する

関数のオーバーロードは、同じ役割の処理を同じ名前でまとめられる便利な機能です。まずは PrintSpawn のように、型違いの処理を統一するところから使ってみてください。

関連記事