関数のオーバーロード(overloading)とは、同じ名前の関数を異なるシグネチャ(パラメータの数や型)を持つ複数の関数として宣言するメカニズムです。
デフォルト引数は、関数の宣言時に指定できます。微妙さ:デフォルト引数は呼び出しのコンパイル時にのみ考慮され、コンパイラは見えるシグネチャに基づいてそれを置き換えます。
クラスでは次のような状況が頻繁に見られます:
class Printer { void print(int n, char c = '*') { /* ... */ } void print(const std::string& s) { /* ... */ } }; Printer p; p.print(5); // print(int n, char c = '*')が呼び出され、cは'*' p.print("Hi"); // print(const std::string&)が呼び出される
微妙さ:
void foo(int x, int y = 10); void foo(int x); foo(1); // エラー:どの関数を呼び出すか不明
クラスメンバー関数のデフォルト引数を指定できる唯一の場所はどこですか?
答え:クラスメソッドのデフォルト引数は、クラス内のメソッド宣言(またはクラスの外部での最初の宣言)で指定することが許可されているが、クラス内部と実装の両方で指定することは許可されていません。そうしないと再定義のエラーが発生します。
class X { void func(int x = 5); // ここで可能 }; void X::func(int x) { /* ... */ } // しかしここではダメ!
物語 1
銀行ソフトウェアでは、コンパイル時にエラーが発生しました:異なるデフォルト引数を持つオーバーロードされた関数が必要なものの明確な選択を妨げ、コンパイル時に呼び出しが曖昧になり、手動修正を行うまでリリースをビルドできないという結果になりました。
物語 2
チームには、すべてのデフォルト引数を実装に置くスタイルが広まりましたが、クラスの一部のメソッドに対しては、インターフェースと実装の間で不一致を引き起こし、異なるTUが異なる関数パラメータを見て、奇妙なコンパイル時およびランタイムバグを引き起こしました。
物語 3
公共ライブラリを拡張する際に、同じパラメータを持つ関数のオーバーロードが異なるデフォルト引数で誤って追加されました。コンパイラはAPI呼び出しの曖昧さを報告し、ユーザーは古い呼び出しや壊れたバイナリ互換性に直面しました。