質問
私は、かなり巨大なコードベースを持つプロジェクトにたどり着きました。
私は主にC ++を扱っており、彼らが書いたコードの多くはブール論理に二重否定を使用しています。
if (!!variable && (!!api.lookup("some-string"))) {
do_some_stuff();
}
私はこれらの人たちが知的なプログラマーであることを知っています、彼らが偶然にこれをしているわけではないことは明らかです。
私はベテランのC ++の専門家ではありません。彼らがこれを行っている理由は、評価される値が実際のブール表現であることを絶対的に肯定したいからです。したがって、彼らはそれを否定し、それを再び否定して、実際のブール値に戻します。
これは正しいですか、それとも何か不足していますか?
解決
ブールに変換するのはコツです。
他のヒント
実際には、一部のコンテキストでは非常に便利なイディオムです。これらのマクロを使用します(Linuxカーネルの例)。 GCCの場合、次のように実装されています。
#define likely(cond) (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))
なぜ彼らはこれをしなければならないのですか? GCCの__builtin_expect
は、そのパラメーターをlong
ではなくbool
として扱います。そのため、何らかの形式の変換が必要です。これらのマクロを書いているときにcond
が何であるかを知らないため、単純に!!
イディオムを使用するのが最も一般的です。
0と比較することでおそらく同じことを行うことができますが、私の意見では、Cが持つboolへのキャストに最も近いので、実際には二重否定を行う方が簡単です。
このコードはC ++でも使用できます...これは最も一般的な分母です。可能であれば、CとC ++の両方で機能することを行います。
コーダーは、オペランドをboolに変換すると考えていますが、これは<!> amp; <!> amp;のオペランドがすでに暗黙的にブールに変換されているため、まったく冗長です。
はい、それは正しいです。 !!
はブールへの変換です。詳細については、この質問をご覧ください。
これは、書き込み(変数!= 0)を回避する手法です。つまり、どの型からでもブールに変換します。
このようなIMOコードは、すぐに読み取り可能なコードではないため、保守する必要のあるシステムには場所がありません(最初の質問です)。
コードは読みやすいものでなければなりません-さもなければ、将来のために時間の借金の遺産を残します-不必要に複雑なものを理解するのに時間がかかるためです。
コンパイラの警告を回避します。これを試してください:
int _tmain(int argc, _TCHAR* argv[])
{
int foo = 5;
bool bar = foo;
bool baz = !!foo;
return 0;
}
「bar」行は<!> quot; boolに「true」または「false」(パフォーマンス警告)<!> quotを強制する値を生成します。 MSVC ++で、しかし 'baz'行はうまく抜けます。
演算子です!過負荷ですか?
そうでない場合、おそらく警告を出さずに変数をブールに変換するためにこれを行っています。これは間違いなく標準的な方法ではありません。
レガシーC開発者はブール型を持たなかったため、多くの場合#define TRUE 1
および#define FALSE 0
を使用し、ブール比較に任意の数値データ型を使用しました。 bool
ができたので、数値型とブール型の混合を使用して特定の種類の割り当てと比較が行われた場合、多くのコンパイラが警告を発します。これらの2つの使用法は、レガシーコードを操作するときに最終的に衝突します。
この問題を回避するために、一部の開発者は次のブールIDを使用します。!num_value
はbool true
if num_value == 0
;を返します。 false
それ以外の場合。 !!num_value
は、bool false
の場合にtrue
を返します。 num_value
それ以外の場合。 static_cast<bool>(num_value)
を(num_value != FALSE)
に変換するには、単一の否定で十分です。ただし、ブール式の元の意味を復元するには、二重否定が必要です。
このパターンはイディオムとして知られています。つまり、この言語に精通している人々がよく使用するものです。したがって、<=>ほど、アンチパターンとは見なしません。キャストによって正しい結果が得られる可能性がありますが、一部のコンパイラーはパフォーマンス警告を出力するため、引き続き対処する必要があります。
これに対処する別の方法は、<=>と言うことです。私もそれで問題ありませんが、全体として、<=>は冗長性がはるかに低く、より明確になる可能性があり、2回目に見たときに混乱することはありません。
Marcin が述べたように、オペレーターのオーバーロードが遊びます。それ以外の場合、C / C ++では、次のいずれかを実行している場合を除いて重要ではありません。
-
true
(またはCではTRUE
マクロのようなもの)との直接比較。これはほとんど常に悪い考えです。例:if (api.lookup("some-string") == true) {...}
-
何かを厳密な0/1値に変換したいだけです。 C ++では、
bool
への割り当てはこれを暗黙的に行います(暗黙的に(some_variable != 0)
に変換可能なものに対して)。 Cでは、または非ブール変数を扱っている場合、これは私が見たイディオムですが、私は自分で<=>多様性を好む。
より大きなブール式のコンテキストでは、単純に物事が煩雑になると思います。
変数がオブジェクトタイプの場合、!演算子は定義されていますが、boolへのキャストはありません(または、さらに悪い意味でintへの暗黙のキャストです。!演算子を2回呼び出すと、boolへの変換は奇妙な場合でも動作します。
!!
は、ブール型を持たない元のC ++に対処するために使用されました(Cもそうではありませんでした)。
問題の例:
if(condition)
の内部では、condition
はdouble, int, void*
などのようなタイプに評価する必要がありますが、bool
はまだ存在しないため、評価する必要はありません。
クラスが存在するint256
(256ビット整数)であり、すべての整数変換/キャストがオーバーロードされているとします。
int256 x = foo();
if (x) ...
x
が<!> quot; true <!> quot;であったかどうかをテストするにはまたはゼロ以外の場合、if (x)
はint
を整数に変換し、 then がその(int) x
がゼロでないかどうかを評価します。 !
の典型的なオーバーロードは、!x
のLSbitのみを返します。 if (!!x)
は、<=>のLSbitのみをテストしていました。
ただし、C ++には<=>演算子があります。オーバーロードされた<=>は、通常、<=>のすべてのビットを評価します。したがって、非反転ロジックに戻るには<=>が使用されます。
Ref C ++の古いバージョンは、` if() `ステートメントで条件を評価するときにクラスの` int`演算子を使用しましたか?
それは正しいですが、Cではここでは意味がありません-'if'および '<!> amp; <!> amp;' 「!!」なしで式を同じように処理します。
C ++でこれを行う理由は、「<!> amp; <!> amp;」だと思う過負荷になる可能性があります。しかし、そうすると '!'になる可能性があるため、variable
およびapi.call
のタイプのコードを見なくても、ブール値を取得することを本当に保証するわけではありません。 C ++の経験が豊富な人が説明できるかもしれません。おそらく、それは保証ではなく、多層防御の手段として意図されています。
プログラマがこのようなことを考えていたのかもしれません...
!! myAnswerはブール値です。コンテキストでは、それはブール値になるはずですが、念のために物事をバングバングするのが好きです。
これは、ダブルバントリックの例です。を参照してください詳細については、セーフブールイディオムをご覧ください。ここで、記事の最初のページを要約します。
C ++には、クラスのブールテストを提供する方法がいくつかあります。
明白な方法は、
operator bool
変換演算子です。
// operator bool version
class Testable {
bool ok_;
public:
explicit Testable(bool b=true):ok_(b) {}
operator bool() const { // use bool conversion operator
return ok_;
}
};
クラスをテストできます
Testable test;
if (test)
std::cout << "Yes, test is working!\n";
else
std::cout << "No, test is not working!\n";
ただし、opereator bool
はtest << 1;
やint i=test
などの無意味な操作を許可するため、安全ではないと見なされます。
暗黙的な変換やオーバーロードの問題を回避するため、Using
operator!
の方が安全です。
実装は簡単です、
bool operator!() const { // use operator!
return !ok_;
}
Testable
オブジェクトをテストする2つの慣用的な方法は
Testable test;
if (!!test)
std::cout << "Yes, test is working!\n";
if (!test2) {
std::cout << "No, test2 is not working!\n";
最初のバージョンif (!!test)
は、一部の人々がダブルバントリックと呼ぶものです。