明示的および暗黙的の定義が標準定義と一致しないため、ここには2つの異なる問題があります(既存の回答のほとんどが基づいており、明示的および暗黙的の独自の定義を含む例を追加する前に記述されています)。
では、最初に明示的な定義について考えてみましょう。これは、(型名を明示的に記述しているため、明示的に呼んでいると思いますか?):
integer int1 = integer(0, 100);
対暗黙の定義は次のようになります。
integer int1(1, 100);
この場合、最初の「明示的な」呼び出しは、実際には2番目の「暗黙的な」呼び出しに勝る利点はありません。しかし、まだ違いがあります。最初のものは、実際には2つの引数のコンストラクターを使用して一時的なものを作成し、次にint1
コピーコンストラクターを使用して作成するために使用されます。実際には、コンパイラは通常、この追加のコピーを最適化しますが、コピーコンストラクタがプライベートの場合は機能しませんが、2番目のコンストラクタは2つの引数のコンストラクタのみを必要とします(これは不利と見なされることもあります)。
しかし、ここで、明示的および暗黙的なの実際の標準定義について説明します。明示的なコンストラクター呼び出しとは、明示的に呼び出すコンストラクター呼び出しです。実際には、括弧構文()
を使用してオブジェクトを作成する場合は常に、コンストラクターを明示的に呼び出します。それ以外の場合は、暗黙的なコンストラクター呼び出しです(つまり、コンパイラーによってバックグラウンドで実行されます)。
integer int1; // implicit default constructor
integer int1(1, 100); // explicit two-arg constructor
integer int1 = integer(0, 100); // explicit two-arg constructor, implicit copy constructor
void func(integer); // function taking by-value
func(int1); // implicit copy constructor
したがって、暗黙的に呼び出すことができるコンストラクターは、デフォルトのコンストラクターと任意の1つの引数のコンストラクター(コピーおよび移動コンストラクターを含む)のみです。この点に関する特別な問題は、1つの引数のコンストラクターがコピー/移動コンストラクターではないことです。
struct integer
{
integer(int);
};
これにより、コンパイラーはコンストラクターを暗黙的に呼び出して型を変換できるため、int
暗黙的に次のように変換できinteger
ます。
void func(integer);
func(42); // implicit call to int-constructor
このような動作を禁止するには、コンストラクターにマークを付ける必要がありますexplicit
。
struct integer
{
explicit integer(int);
};
これは、明示的に呼び出すことだけを許可します(たとえばfunc(integer(42))
)(しかし、あなたはすでにこれを知っていると思います)。これには、バックグラウンドで見過ごされたり不要な変換が発生したりすることがないという利点があります。これにより、過負荷の解決に関するあらゆる種類の見つけにくい問題やあいまいさが生じる可能性があります。したがって、変換コンストラクター(1引数の非コピー/移動コンストラクター)をマークするのが通常の方法であり、おそらくC++11が最終的に変換演算子explicit
を導入した理由でもあります。explicit
したがって、要約すると、あなたの定義と例によれば、(通常は無関係な)違いはありますが、integer int1 = integer(1, 100);
の代わりに使用することに実際には利点はありません。integer int1(1, 100);
しかし、標準の定義によれば、明示的なコンストラクター呼び出しは暗黙的なコンストラクター呼び出しよりも多くの利点があります。オブジェクトを明示的に構築する唯一の方法は、明示的なコンストラクター呼び出しを使用することですが、暗黙的なコンストラクター呼び出しは特定の舞台裏でのみ実行されるためです。状況に応じて、引数が0および1のコンストラクターでのみ機能します(asheplerがすでに指摘しているように)。また、変換コンストラクターを明示的にマークexplicit
することには、不要な暗黙の変換をバックグラウンドで禁止するという利点があります。