有害と見なされる暗黙の変換。
さて、これは少し厳しいかもしれません:
潜在的に危険で高価な暗黙の変換は、有害と見なされます。
暗黙の変換は、その性質上「偶発的に」発生するため、発生した場合は常に正しいことを行う必要があります。
暗黙の変換を防ぐ方法は?簡単: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
の実装にどれだけの時間を費やしたかによって異なります メンバー関数