問題の歴史
C++は最初から関数オーバーロードをサポートする言語として設計されており、同じ名前の関数を異なるパラメータで宣言することができます。これにより、読みやすく使いやすいAPIを構築することが可能になります。
問題
同じ名前の関数が多くなると、コンパイラは引数の型、変換、デフォルト引数、参照を考慮してすべてのオーバーロードされた関数から適切な実装を選択する必要があります。ずさんなオーバーロードは曖昧さや捕まえにくいエラーを引き起こします。
解決策
コンパイラは、引数のシーケンス、型の一致の精度、最小限の変換に基づいて関数を選択します。ただし、重要な変換やデフォルト引数がある場合、予期しない曖昧さが生じることがあります。
コード例:
void foo(int x); void foo(double x); void foo(int x, int y = 0); foo(5); // void foo(int x)が呼び出されます。これは正確な一致です。 foo(5.2); // void foo(double x)が呼び出されます。 foo(5, 6); // void foo(int x, int y)が呼び出されます。
主な特徴:
戻り値の型だけで関数をオーバーロードすることは可能ですか?
いいえ。オーバーロードはパラメータの型と数によってのみ可能であり、戻り値の型はオーバーロード解決には参加しません。
int foo(); double foo(); // エラー:戻り値の型のみでのオーバーロードは不可能です!
すべてのパラメータが変換可能な場合、コンパイラはどのオーバーロードされた関数を呼び出すかどうやって選びますか?
コンパイラは「最高の一致」を選択します。これは、最小限の型変換が必要な関数、または正確な一致です。曖昧さが存在する場合、コードはコンパイルされません。
void bar(int); void bar(long); bar(1); // intの正確な一致:bar(int)が呼び出されます。
デフォルト引数によるオーバーロードと通常のオーバーロードを混ぜることは可能ですか?
可能ですが、関数のシグネチャが重複する場合、呼び出しの曖昧さを生じる可能性があります。
void test(int x); void test(int x, int y = 10); test(5); // エラー:曖昧さ — 両方が適合します。
ライブラリ内で重複するデフォルト引数を持つオーバーロード関数が見られ、コードの更新時にコンパイルエラーが発生します。
利点:
欠点:
プロジェクト内での合意 — デフォルト引数を持つオーバーロードを混合しない、またはデフォルト値を持つ関数を1つだけ使用し、またはユニークなパラメータで純粋にオーバーロードすること。
利点:
欠点: