[解決済み] オーバーロードされた && と || がショートしないのは、実は理由があるのでしょうか?
質問
演算子の短絡挙動について
&&
と
||
はプログラマにとって素晴らしいツールです。
しかし、なぜオーバーロードするとこの動作が失われるのでしょうか?演算子は単に関数のための構文上の糖分であることは理解していますが、その演算子が
bool
の演算子にはこの動作があるのに、なぜこの単一の型に限定されなければならないのでしょうか?この背後にある技術的な理由があるのでしょうか?
どのように解決するのですか?
すべての設計プロセスは、相互に互換性のない目標間の妥協に帰結します。 残念ながら、オーバーロードされた
&&
演算子の設計過程では、最終的に混乱した結果が生じました。
&&
-- の特徴である短絡的な動作が省略されてしまうということです。
その設計プロセスがどのようにこの不幸な場所に行き着いたか、その詳細は私にはわかりません。しかし、後の設計プロセスがこの不運な結果をどのように考慮したかを見ることは、関連性があります。 C# では、オーバーロードされた
&&
演算子
は
を短絡させる。C#の設計者はどのようにしてそれを実現したのでしょうか?
他の回答の1つは、"lambda lifting"を提案しています。つまり
A && B
は道徳的に等価なものとして実現されうる。
operator_&& ( A, ()=> B )
ここで、2番目の引数は遅延評価のための何らかのメカニズムを使用し、評価されたときに、式の副作用と値が生成されるようにします。オーバーロードされた演算子の実装は、必要なときだけ遅延評価を行うことになります。
これは C# の設計チームが行ったことではありません。(余談ですが、ラムダリフティングは
は
をするときにやったこと
式木表現
の
??
演算子で、特定の変換操作を遅延的に実行する必要があります。しかし、その詳細を説明することは大きな脱線になります。ラムダ リフティングは機能しますが、十分にヘビー級であるため、それを回避することを希望しました)。
むしろ、C# ソリューションは問題を 2 つの別々の問題に分解します。
- 右側のオペランドを評価する必要があるか?
- 上記の答えが "yes"であった場合、2 つのオペランドをどのように結合するのでしょうか?
したがって、この問題はオーバーロードを違法とすることで解決されます。
&&
を直接オーバーロードすることを違法とすることで問題は解決します。むしろ、C#では、オーバーロードする必要がある
に
演算子をオーバーロードする必要があります。
class C
{
// Is this thing "false-ish"? If yes, we can skip computing the right
// hand size of an &&
public static bool operator false (C c) { whatever }
// If we didn't skip the RHS, how do we combine them?
public static C operator & (C left, C right) { whatever }
...
(余談:実は3つあります。C#では、もし演算子
false
が提供されている場合、演算子
true
という質問に答える演算子も必要です。通常、このような演算子を1つだけ提供する理由はないため、C#は両方を要求します)。
形式のステートメントを考えてみましょう。
C cresult = cleft && cright;
コンパイラはこれを擬似C#で書いたかのようにコードを生成します。
C cresult;
C tempLeft = cleft;
cresult = C.false(tempLeft) ? tempLeft : C.&(tempLeft, cright);
見ての通り、左辺は常に評価されます。もしそれが "false-ish" であると判断されれば、それが結果となります。そうでなければ、右辺が評価され、その結果
を熱望
ユーザ定義演算子
&
が呼び出されます。
は
||
演算子は同様の方法で定義され、演算子trueの呼び出しとして、熱心な
|
演算子の呼び出しとして定義されます。
cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
4つの演算子すべてを定義することで --
true
,
false
,
&
と
|
-- C#では、単に
cleft && cright
だけでなく、短絡的でない
cleft & cright
も、また
if (cleft) if (cright) ...
とか
c ? consequence : alternative
と
while(c)
といった具合に。
さて、すべての設計プロセスは妥協の産物であると申し上げました。ここでC#言語の設計者は、なんとか短絡的に
&&
と
||
のような、正しい値を設定する必要がありますが、そのためには
4
演算子の代わりに
二
の代わりに演算子を使っているため、混乱する人もいるようです。演算子true/falseの機能は、C#の機能の中で最も理解されていない機能の1つです。C++ユーザーに馴染みのある、賢明で分かりやすい言語を目指すという目標に対して、短絡的な表現をしたいという要望や、ラムダリフティングなどの遅延評価を実装したくないという要望がありました。それはそれで妥当な妥協点だったと思いますが、重要なのは、それが
は
は妥協の産物です。ちょうど
とは異なる
C++ の設計者がたどり着いた妥協点とは異なります。
このような演算子の言語設計の話題に興味があるなら、C# が null 可能なブール値に対してこれらの演算子を定義しない理由についての私のシリーズを読むことを検討してください。
http://ericlippert.com/2012/03/26/null-is-not-false-part-one/
関連
-
[解決済み】 unsigned int vs. size_t
-
[解決済み】文字列関数で'char const*'のインスタンスを投げた後に呼び出されるterminate [閉店].
-
[解決済み】IntelliSense:オブジェクトに、メンバー関数と互換性のない型修飾子がある
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み】指定範囲内の乱数で配列を埋める(C++)
-
[解決済み】デバッグアサーションに失敗しました
-
[解決済み] template "と "typename "キーワードはどこに、なぜ入れなければならないのですか?
-
[解決済み] SQLのWHERE句は短絡的に評価されるか?
-
[解決済み] 論理演算子の短絡は必須ですか?また、評価順序は?
-
[解決済み] JavaScriptには "Short-circuit "評価があるのか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】関数名の前に期待されるイニシャライザー
-
[解決済み] 非常に基本的なC++プログラムの問題 - バイナリ式への無効なオペランド
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み] 式はクラス型を持つ必要があります。
-
[解決済み】#include<iostream>は存在するのですが、「識別子 "cout "は未定義です」というエラーが出ます。なぜですか?
-
[解決済み】浮動小数点数の乱数生成
-
[解決済み】クラスのコンストラクタへの未定義参照、.cppファイルの修正も含む
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み】VC++の致命的なエラーLNK1168:書き込みのためにfilename.exeを開くことができません。
-
[解決済み] 配列のベクトルを扱う正しい方法