これは私の比較シリーズの第 2 部であるはずでしたが、ほぼ完成しましたが、大学のもののために、それを磨く時間がありませんでした.
しかし、オプションのディスカッションが再び始まったので、本当にしたかっただけです このトピックに関する私の生の考えをすぐに共有してください。運が良くて、私の言いたいことがわからない場合:03
代入の振る舞いが (実際にはそうであるにもかかわらず) 明確ではなかったため、現在はコンパイルできません。基本的に、私が答えたい議論には次の 4 つの質問があります。
13
です ポインタと同じですか?21
は必要ですか ?tl;dr:いいえ、しません、再バインドします、いいえ。
1. 38
です ポインタと同じ?
「オプションの43
」を持つことは何を意味しますか 」?まあ、それは 58
です 60
の場合もあります .
ポインタ、73
?
いいえ、そうではありません。
89
の間にはもっと重要な違いがあります と 93
nullability に加えて:A 100
暗黙の作成とアクセス、111
があります。 明示的な作成とアクセス。
オブジェクトがある場合は、それへの参照をサイレントにバインドできます。また、参照がある場合は、それを あたかも 扱うことができます。 それはオブジェクトでした。ポインターの場合は、明示的に 126
を使用する必要があります と 133
.
そして、この違いは非常に大きいです:それは 143
を意味します 追加の構文の問題なしで関数パラメーターに使用できます:
void print(const T& obj);
…
T obj = …;
print(obj);
152
は使いたくないでしょう 呼び出し側は余分な作業を行う必要があるため、不要な 165
を使用する必要があります .これは厄介です。
したがって、当然のことながら、オプションの引数が必要な場合は、同じ理由でポインターを使用したくないでしょう。なぜ今、不要な構文オーバーヘッドを導入するのでしょうか?それは呼び出し元にとって重要ではありません。
だから 175
189
と同じではありません :明示的ではなく、暗黙的な作成構文があります。
何 193
持つことはできませんが、暗黙的なアクセスです。現在実装できないだけでなく、基本的に不可能です:208
の場合 暗黙的なアクセス構文を持つために、それに対するすべての操作は参照オブジェクトに委任されます.これには、オブジェクトを参照しているかどうかのチェックが含まれます!Any 219
または 228
これは、明示的な構文が必要であることを意味します。それ以外の場合は、オプションに null のオブジェクトがあるかどうかを確認するだけです。
より徹底的な分析は、今年初めに開催された C++Now での Rethinking Pointers の講演の最初の 20 分間で見つけることができます。
2. 231
は必要ですか ?
241
として 254
と同じではありません 、 268
を使用する状況を調べる必要があります そこにオプションのバージョンが必要かどうかを考えてください。
幸いなことに、私は Rethinking Pointers の講演でまさにそれを行いました。
関数パラメータ
void print(const T& obj);
void sort(Container& cont);
ここでは、コピーを避けるか、引数をインプレースで変更したいと考えています。オプションの引数が必要な場合は、278
が解決策です。ただし、単純に関数をオーバーロードすることも同様に機能します。
ゲッター関数
const std::string& person::name() const;
繰り返しますが、コピーは避けたいと考えています。
戻り値が利用できない場合は、非参照 280
を使用できます 、しかし追加のコピーを支払う必要があります。または、連絡先を絞り込んで、オブジェクトがそこにあることを必要とする前提条件を追加することもできますが、これはタイプセーフではありません.
LValue 関数
T& std::vector::operator[](std::size_t index);
T& std::optional<T>::value();
ここでは絶対に 戻り値の型として左辺値が必要です。これが参照の背後にある動機であるため、それらを使用します。ただし、オプションの参照は機能しません。演算子の従来の使用法と互換性のない暗黙的なアクセスが失われます。
範囲ベースの 292
ループ
for (auto& cur : container)
…
ここではオプションの参照は必要ありません。
関数呼び出し時の寿命延長 (エキスパートのみ):
const std::string& name = p.name();
// use `name` multiple times
ライフタイム延長は、通常の参照でのみ機能します。
301
を使用する必要がある状況は以上です。 .317
を使用できる唯一の状況 コピーを避けたい関数パラメータと getter です。これはそれほど説得力のある使用例ではありません。
3.代入演算子は再バインドまたは代入スルーする必要がありますか?
課題根本的に はコピーの最適化です。「現在のオブジェクトを破棄する」および「新しいオブジェクトをコピーする」と同じことを行うだけです。
320
と書くと 、それは 333
を変更します 347
のコピーです .これはすべての 351
に当てはまります 、360
を含む :If 378
384
への参照です 、次に 396
406
への参照にもなります 、 415
への参照であったとしても そのため、コピー代入演算子は再バインド操作を行います。
今426
434
を取る代入演算子もあります :この代入演算子は、442
を取るコンストラクターの最適化です .
そのため、現在のオブジェクトが存在する場合は破棄し、その中に新しいオブジェクトを作成します。ただし、これは最適化であるため、454
を使用します。 オプションにすでに値がある場合。 469
の代入演算子 「破壊」に続いて「構築」するよりも効率的かもしれません。
ただし、472
の代入演算子が コピーの最適化です! 482
を指定すると どこで 495
「ロケットを発射する」を意味しますが、これは失敗します。しかし、これはオプションのせいではありません。あなたのタイプは愚かです!
そのような愚かなタイプの 1 つが 506
です。 :516
の代入演算子 「destroy」に続く「copy」の最適化ではありません。これは、参照に代入演算子がないためです。 :参照に対して行うすべての操作は、実際にはそれが参照するオブジェクトに対して行われます.これには代入が含まれるため、代入演算子は値を代入します.
今では、525
でその動作をしていると考える人もいます。 539
の
そうではありません。
他の反論を無視すると、これらのセマンティクスは 542
として混乱を招きます 551
の状態に応じて、まったく異なることを行います !
std::optional<T&> opt = …;
T obj;
opt = obj;
// if opt was empty before, it will now refer to obj
// if opt wasn't empty before, it will now refer to an object with the same value as obj
return opt; // so this is legal only if the optional wasn't empty before
代入演算子はこのように振る舞うべきではないため、このように振る舞う代入演算子の前例はありません。
4.代入演算子も必要ですか?
568
を使用するときはいつでも 参照自体を変更する必要はありません – 結局、できません。 585
で 599
を変更する必要はありません
今度は 604
の「アサインスルー」の人々 この動作は 615
と一致していると主張します .
参照は割り当てられないため、そうではありません。
はい、628
を書いています コンパイル ですが、割り当てではありません。すべての理由でのみ機能します 参照に対して行われる操作は、それが参照するオブジェクトに対して行われます。
前に言ったように、null 許容参照がある場合、それはできません。なぜなら、null 許容性をチェックする構文がないからです。したがって、630
と完全に一致する唯一の方法は、 644
の場合 656
を持つべきではありません。 、664
関数など。結局、671
は不変なので、686
691
を変異させる必要がある場合 、あなたは欲しくなかった 701
、ポインターが必要でした。なぜなら、オプションを永続的な場所に保存し、それを明確にするために明示的な作成構文を使用する必要があったからです。それについては、私の話で詳しく説明します。
716
がある場合は注意してください 修飾子がないと、722
のようには動作しません。 – 736
だから 743
のように振る舞わない .ジェネリック コードが 754
を処理できないのと同じように 、 769
も処理しません .
したがって、「optional 774
」と綴るべきではありません。 」として 786
795
と呼ぶべきだと思います。
結論
私の意見では、808
は必要ありません .これは、使用例がほとんどない奇妙なタイプです。
委員会が 818
の追加を決定した場合 努力する価値があります。不変の 822
でなければなりません 836
の実際の使用例については 、 841
の使用例と同じように 、それは実際には問題ではありません。
850
のように振る舞う型に注意してください。 ですが、そうではありませんが、役に立ちます:A 869
はさまざまなことを行うことができるので、それが行うことの 1 つだけを明示的にモデル化する個別の型を追加することをお勧めします。たとえば、私の type_safe ライブラリには 871
があります。 、これは 884
のようなものです nullable 897
とは異なります ただし、901
のように綴ってはいけません。 919
ではないため .
詳細については、私の Rethinking Pointers の講演をご覧ください。