明示的なコンストラクターを作成しますが、代入はどうでしょうか?

有害と見なされる暗黙の変換。

さて、これは少し厳しいかもしれません:

潜在的に危険で高価な暗黙の変換は、有害と見なされます。

暗黙の変換は、その性質上「偶発的に」発生するため、発生した場合は常に正しいことを行う必要があります。

暗黙の変換を防ぐ方法は?簡単:05 を使用する コンストラクター。

しかし、それは問題の半分にすぎません:代入についてはどうですか? 13 はありますか? その場合、いつ使用しますか?

28 の規則

まず、32 について話しましょう コンストラクタの詳細。

単一引数のコンストラクターを 45 としてマークすると、おそらくそれがわかるでしょう。 、暗黙の変換では使用できません:

struct foo
{
  // explicit construction from int
  explicit foo(int i);

  // implicit construction from const char*
  foo(const char* p);
};

void take_foo(foo f);

…

take_foo(0);         // error: no implicit construction
take_foo(foo(0));    // okay
take_foo("hi");      // okay, implicit allowed
take_foo(foo("hi")); // allowed as well

あなたが知らないかもしれないことは、何でもマークできるということです 58 としてのコンストラクター 、任意の数のパラメータ:

struct foo
{
  explicit foo(int a, int b); // okay
  
  template <typename ... Args>
  explicit foo(Args... args); // okay

  explicit foo(); // okay

  explicit foo(const foo& other); // okay, but you really shouldn't do that
};

明らかに、これらのコンストラクターは暗黙的な変換には使用できないため、明示的なものは別の意味も持つ必要があります。 コンストラクターは コピーの初期化 には使用できません .

とは コピーの初期化?

C++ での無数の初期化方法については説明しません。そのため、以下はコピー初期化の簡単な抜粋に過ぎません。コピー初期化は、77 で変数を初期化するときに発生します。 (85 のように) ) ですが、関数呼び出し、return ステートメント、および 95 にも使用されます そして 100 (しかし、最後の 2 つは 118 にとっては重要ではありません - それらがそうする場合を除いて)。 コンストラクター。

これにより、136 の一般化されたルールが可能になります :コンストラクタが 140 とマークされている場合 、そのコンストラクターを使用するには、型を指定する必要があります。159 コンストラクターは、タイプが明示的に「近くにある」と言及されていないコンテキストでは使用できません:

struct foo
{
    explicit foo(int) {}
};

foo a(0); // type nearby
foo b{0}; // type nearby
foo c = foo(0); // type nearby

foo d = 0; // type not nearby enough
foo e = {0}; // type not nearby enough

foo function()
{
    return 0; // type far away
}

いつ 164 を使用するか コンストラクタ?

上記の一般化に基づいて、答えは驚くほど簡単です:178 を使用してください その型のオブジェクトを作成するときに、ユーザーにその型の名前を書き込んでもらいたいときはいつでもコンストラクターを使用してください。

特に、引数が 1 つのコンストラクターの場合:引数が 1 つのコンストラクターを 183 としてマークします。 ただし、そのコンストラクターに前提条件がない場合、実行時のオーバーヘッドが高くない場合、または何らかの理由で暗黙の構築が望ましいと思われる場合を除きます (最後のものは専門家専用です)。

2 番目の規則は暗黙的な変換を防ぐために重要ですが、最初の規則も「複数引数の暗黙的な変換」を防ぐために役立ちます。

たとえば、194 があるとします。 次のコンストラクタを持つクラス:

rational(int num, int den);

203 としてマークすることもできます 215のように感じたら パラメータが有理数の場合は許可されません。

しかし、226 を使っている人は見たことがありません 常に複数の引数を必要とするコンストラクターの場合、その有用性について十分なデータがありません.

ただし、デフォルトのパラメーターを持つコンストラクターがある場合、問題が発生することに注意してください:

foo(int i, float f = 3.14);

そのコンストラクターは暗黙の変換に使用できるため、239 が必要です。 .ただし、これを 243 としてマークします 引数が 2 つの場合にも適用されるため、253 を防ぐことができます。 、たとえば。これはおそらく望ましくありません。

非標準 265

では、277 について話しましょう .

コピー/移動割り当て演算子の場合、それらとコピー/移動コンストラクターの間に対称性がある必要があります。特に、286 を指定すると、 タイプ 292 の 、これ

T obj(other_obj);

と同等であるべきです

T obj; // assume default constructor here
obj = other_obj;

しかし、305 の場合はどうなるでしょうか タイプ 318 を持っています - その場合、動作は同等である必要がありますか?

322 の作成に使用されるコンストラクターに依存します 336 が与えられた場合 、つまり、そのコンストラクターが 345 であるかどうか .

353 コンストラクターと 369

370 以外がある場合 381 を取るコンストラクタ 、その後、同等の動作があるはずです.結局のところ、次のように書くこともできます:

T obj = other_obj;

ばかげている 、プレーン 390 の場合 許可されませんでした。

そして、これは追加の作業を行わなくても、言語によって既に保証されています。代入演算子は、一時的な 401 を作成します 暗黙的な変換を使用してオブジェクトを作成し、移動代入演算子を呼び出します。

その操作のコストは余分な移動割り当てであり、コストがゼロではない可能性があり、さらに重要なことに、より効率的な割り当ての実装が可能になる可能性があります。

418 を検討してください たとえば、 427 がないとします。 438 を取る 暗黙のコンストラクタのみです。次に、次のコードを記述します:

std::string str = "abcde";
str = "12345";

小さな文字列の最適化を無視して、最初の行で暗黙のコンストラクターを呼び出し、5 文字分のメモリを割り当て、444 をコピーします。 457 がないため、2 行目で別の文字列を割り当てます。 一時的な 467 に直接適用可能 暗黙のコンストラクターを使用して作成されます。これにより、再度メモリが割り当てられます。その後、移動代入演算子が呼び出されるため、470 最近割り当てられたメモリの所有権を取得し、自身のメモリを解放します。

しかし、2 番目のメモリ割り当ては不要でした!480 すでに割り当てられているバッファーに収まるので、より効率的な割り当てでは、文字列をコピーするだけです。幸いなことに、491 509 などのより効率的な割り当てを提供します 514 を取る !

あなたの課題もそうなら、527 と書いてください。 536 を取る .

543 コンストラクターと 559

では、コンストラクターが 569 を取るとしましょう 573 です .割り当てを許可しますか?

答えはノーです。

589 を取る代入演算子を書くと 、あなたは 590 を許可します .しかし 602 614 は違法です! 代入とは何の関係もありません。C++ には奇妙な初期化形式が多すぎるだけです。これは矛盾です。つまり、一貫性がないため、発生するべきではありません。

628 をどのように割り当てますか 631 に異議を唱える それでは?649のルールに従います タイプを指定します:656 .

ただし、これには暗黙のコンストラクターと同じ問題があります。コードはさらに… 661 .あなたはまだ一時的な + 移動の費用を支払う必要があり、より効率的な割り当ての実装を使用することはできません.

676 ならいいですね 割り当ては直接サポートされます.An 685 691 を書き込むときに代入演算子が呼び出されます - コンストラクターではありません - 707 によるものではありません 、したがって、710 のままで、より効率的な割り当てを行うことができます .しかし、その機能はありません.

したがって、728 をオーバーロードする場合 不整合につながり、オーバーヘッドに過負荷をかけません:どうすればよいですか?

代入を実装するには複数の方法があります - 739 は必要ありません :メンバ関数 744 を書く 759 を取る 761 を使用して割り当てます .これは醜いですが、最善の解決策です。

複数引数のコンストラクターと 770

複数引数のコンストラクターと 783 についてはどうですか ?もちろん、多引数代入の構文はありません。右辺の引数は 1 つだけです。

ただし、複数引数の関数呼び出しには制限がないため、793 を記述できます。 1 つ以上の引数を取る関数です。それでもいいですか?

これもまた、一時的なプラス移動代案のコストに依存します.If 807 もっと安くできるかもしれません。実装してください。繰り返しますが、811 826 を提供 まさにその理由でコンストラクターに一致する関数。

結論

要約すると:

このコンストラクターを 834 としてマークする必要がありますか? ?

  • 単一引数のコンストラクタの場合 → ほとんどの場合、はい
  • そうでない場合 → コピーの初期化が本当に嫌いでない限り、おそらくそうではありません

844 と書くべきですか 856 を取る ?

  • そうでないコンストラクタがある場合 869 872 を受け取ります → 暗黙の一時 + 移動よりも効率的に実行できる場合は、記述することを検討してください
  • そうでなければ→いいえ

886 と書くべきですか 894 を取るメンバ関数 ?

  • 906 を取るコンストラクタがない場合 →おそらくそうではない
  • 仮設+移動よりも効率的にできるなら→検討
  • If 910 、そのコンストラクターは 929 です 、および 一時+移動より効率的 → より強く検討
  • Else → さまざまな 935 の実装にどれだけの時間を費やしたかによって異なります メンバー関数