C++ では、コンストラクタが1つの引数で呼び出されることができる場合、デフォルトで implicit(暗黙的)とみなされます。このようなコンストラクタは 暗黙の型変換 に使用できます。これを防ぐために explicit キーワードが使用されます。
explicit コンストラクタは暗黙的な変換を禁止します。explicit を使用することで、例えば型が異なる引数を関数に渡すときや変数を初期化するときに、予期しない変換を避けられます。
例:
struct Foo { explicit Foo(int x) { /* ... */ } }; Foo a = 10; // エラー、explicit により暗黙の初期化が禁止 Foo b(10); // OK
もし explicit がなければ、Foo a = 10; の式が許可され、予期しないバグにつながる可能性がありました。
質問: explicit で宣言されたすべてのコンストラクタは = での初期化で呼び出すことができないのか?
一般的な回答: はい、explicit はすべての初期化を禁止します。
正しい回答: explicit は暗黙的な変換のみを禁止します。直接初期化(ClassName obj(param);)では explicit コンストラクタが呼び出されます。コピー初期化(ClassName obj = param;)では呼び出されません。
例:
struct A { explicit A(int) {} }; A x = 1; // エラー A y(1); // OK
物語: プロジェクトで explicit なしで書かれたコンストラクタにより、型を介した初期化が許可され、関数の引数に自動的な変換が発生しました。これが隠れたバグの原因となり、デバッグが困難になりました。
物語: 開発者がコンテナのコンストラクタに explicit を付けず、デフォルトコンストラクタが突然他の型の割り当てや渡しの際に呼び出されました。結果 — オブジェクトの作成ロジックが不正確になり、予測できない動作が発生しました。
物語: 未熟なプログラマーが explicit コンストラクタを宣言しましたが、丸括弧による直接初期化が機能することに驚き、explicit がそれもブロックすると考えていました。これにより、安全な便利なパターンを使用せず、プロジェクトに余計なコードが増えました。