C ++での二重否定


124

かなり巨大なコードベースのプロジェクトにたどり着きました。

私は主にC ++を扱っており、彼らが作成するコードの多くはブールロジックに二重否定を使用しています。

 if (!!variable && (!!api.lookup("some-string"))) {
       do_some_stuff();
 }                                   

私はこれらの人がインテリジェントなプログラマであることを知っています、彼らがこれを偶然にしていないのは明らかです。

私はベテランのC ++エキスパートではありません。なぜ彼らがこれを行っているのかという私の唯一の推測は、評価されている値が実際のブール表現であることを絶対的に肯定したいということです。したがって、彼らはそれを否定し、それを再び否定して、実際のブール値に戻します。

これは正しいですか、それとも何か不足していますか?


4
ここをチェックして、すでに質問されています、です!! C ++でブールに変換する安全な方法?
Özgür

このトピックについては、ここで説明しました
Dima

1
の重複の可能性があります!! C ++でブールに変換する安全な方法?
EdChum

回答:


121

boolに変換するトリックです。


19
(bool)で明示的にキャストするとより明確になると思います。タイピングが少ないので、なぜこのトリッキーな!!
Baiyan Huang

27
しかし、それはC ++や最近のCでは意味がなく、結果がブール式でのみ使用されます(質問のように)。boolタイプがなかったときに、ブール変数以外の値10ブール変数に値を格納しないようにするのに役立ちました。
マイクシーモア

6
@lzprgmr:明示的なキャストにより、MSVCで「パフォーマンス警告」が発生します。問題を使用!!または!=0解決します。2つのうちの1つは前者よりもわかります(より多くの型で機能するため)。また、問題のコードでどちらも使用する理由がないことに同意します。
Yakov Galka

6
@Noldorin、それ読みやすさを改善すると思います-あなたがそれが何を意味するかを知っているなら、それはシンプルで端正で論理的です。
jwg 2014年

19
改善?血まみれの地獄...あなたが喫煙しているものをいくつか私にくれ。
Noldorin 2014年

73

実際には、状況によっては非常に便利なイディオムです。これらのマクロ(Linuxカーネルの例)を取り上げます。GCCの場合、次のように実装されます。

#define likely(cond)   (__builtin_expect(!!(cond), 1))
#define unlikely(cond) (__builtin_expect(!!(cond), 0))

なぜ彼らはこれをしなければならないのですか?GCC __builtin_expectはそのパラメータをlongではなくとして扱うboolため、なんらかの形式の変換が必要です。cond彼らはそれらのマクロを書いているときに何をしているのかわからないので、単純に!!イディオムを使用するのが最も一般的です。

彼らはおそらく0と比較することで同じことを行うことができますが、私の意見では、Cが持っているブール値へのキャストに最も近いので、二重否定を行うほうが実際にはより簡単です。

このコードはC ++でも使用できます...それは最低限の共通点です。可能であれば、CとC ++の両方で機能するものを実行してください。


これをじっくりと考えると、これは非常に理にかなっていると思います。すべての回答を読んだわけではありませんが、変換プロセスが指定されていないようです。高さが2ビットの値と高さが1ビットのみの値がある場合、ゼロ以外の値になります。ゼロ以外の値を否定すると、ブール変換になります(ゼロの場合はfalse、それ以外の場合はtrue)。次に、再び否定すると、元の真理を表すブール値が得られます。
ジョーイカーソン

SOではコメントを更新できないため、間違いを修正します。整数値を否定すると、ブール変換になります(ゼロ以外の場合はfalse、それ以外の場合はtrue)。
ジョーイカーソン

51

コーダーは、オペランドをブール値に変換すると考えていますが、&&のオペランドはすでに暗黙的にブール値に変換されているため、まったく冗長です。


14
Visual C ++では、このトリックがないとパフォーマンスが低下する場合があります。
キリルV.リャドビンスキー2012年

1
コード内の無用な警告を回避するよりも、警告を無効にする方が良いと思います。
ルスラン

おそらく彼らはそれを理解していません。ただし、これはマクロのコンテキストでは完全に理にかなっており、気づいていない整数データ型を扱うことができます。括弧演算子がオーバーロードされているオブジェクトがビットフィールドを表す整数値を返すことを考慮してください。
ジョーイカーソン

12

これは(変数!= 0)を書くことを避けるための手法です。つまり、どんな型からもブールに変換します。

このようなIMOコードは、すぐに読み取り可能なコードではないため(最初から問題なので)、保守が必要なシステムには場所がありません。

コードは読みやすくする必要があります。そうしないと、不必要に複雑なものを理解するのに時間がかかるため、将来に向けて時間借金の遺産を残します。


8
私のトリックの定義は、誰もが最初に読んでも理解できるものではありません。理解する必要がある何かはトリックです。また、恐ろしいです!オペレーターが過負荷になる可能性があります...
Richard Harrison、

6
@ orlandu63:単純な型キャストはbool(expr)、正しいことを行い、誰もがその意図を一目で理解します。!!(expr)二重否定であり、誤ってブール値に変換されます...これは単純ではありません。
エイドリアン・プリソン

12

はいそれは正しいです、そしてあなたは何かを逃してはいません。 !!ブールへの変換です。詳細については、この質問を参照してください。


9

コンパイラの警告を回避します。これを試して:

int _tmain(int argc, _TCHAR* argv[])
{
    int foo = 5;
    bool bar = foo;
    bool baz = !!foo;
    return 0;
}

「bar」行は、MSVC ++で「ブール値を「true」または「false」に強制する(パフォーマンス警告)」を生成しますが、「baz」行は細かくこっそり入ります。


1
boolタイプについて知らないWindows API自体で最も一般的に発生します-すべてはとして、0または1でエンコードされintます。
Mark Ransom 2012年

4

オペレーターです!過負荷?
そうでない場合、おそらく警告を出さずに変数をブール値に変換するためにこれを行っています。これは間違いなく標準的な方法ではありません。


4

従来のCの開発者は、彼らはしばしばので、何のブールタイプがなかった#define TRUE 1し、#define FALSE 0その後、ブール比較のために、任意の数値データ型を使用します。これでbool、数値型とブール型の混合を使用して特定のタイプの割り当てと比較が行われると、多くのコンパイラが警告を発します。これらの2つの使用法は、レガシーコードを操作するときに最終的に衝突します。

この問題を回避するには、いくつかの開発者は次のブールアイデンティティを使用します。!num_value戻りbool true場合はnum_value == 0falseさもないと。!!num_value次のbool false場合に戻りますnum_value == 0trueさもないと。単一の否定はに変換するnum_valueには十分boolです。ただし、ブール式の元の意味を復元するには、二重否定が必要です。

このパターンはイディオムとして知られています。つまり、この言語に精通している人々が一般的に使用しているものです。したがって、私はそれをアンチパターンとは思っていませんstatic_cast<bool>(num_value)。キャストによって正しい結果が得られる可能性は非常に高いですが、一部のコンパイラーはパフォーマンス警告を出すため、それでも対処する必要があります。

これに対処するもう1つの方法は、と言うことです(num_value != FALSE)。私もそれで大丈夫ですが、全体的に言って、!!num_valueそれほど冗長ではなく、明確である可能性があり、2回目に表示されたときに混乱することはありません。


2

!! ブール型を持たない元のC ++に対処するために使用された(Cもそうでなかったように)。


問題の例:

内部はif(condition)conditionのようないくつかのタイプに評価する必要があるdouble, int, void*など、ではなく、boolそれはまだ存在していないよう。

クラスが存在しint256(256ビット整数)、すべての整数変換/キャストがオーバーロードされたとしましょう。

int256 x = foo();
if (x) ...

テストにあればx「真」または非ゼロであった、if (x)変換しますxいくつかの整数にし、その後、それがどうかを評価int非ゼロでした。の典型的なオーバーロードは(int) xのLSbitsのみを返しxます。 if (x)その後、LSbitsのみをテストしていましたx

しかし、C ++には!演算子があります。過負荷!xは通常、のすべてのビットを評価しますx。したがって、非反転ロジックに戻るためにif (!!x)使用されます。

参照C ++の古いバージョンは、 `if()`ステートメントで条件を評価するときにクラスの `int`演算子を使用しましたか?


1

以下のようマルチンは言及演算子のオーバーロードが劇中であれば、それも重要であります。それ以外の場合は、C / C ++では、次のいずれかを実行している場合を除いて問題になりません。

  • との直接比較true(またはCではTRUEマクロのようなもの)は、ほとんど常に悪い考えです。例えば:

    if (api.lookup("some-string") == true) {...}

  • あなたは単に何かを厳密な0/1の値に変換したいだけです。C ++では、aへの代入boolはこれを暗黙的に行います(暗黙的にに変換可能なものに対してbool)。Cでは、または非ブール変数を扱っている場合、これは私が見たイディオムですが、私は(some_variable != 0)多様性を自分で好みます。

より大きなブール式の文脈では、それは単に物事を混乱させると思います。


1

変数がオブジェクト型の場合、!演算子は定義されていますが、boolへのキャストはありません(さらに悪いことに、異なるセマンティクスを持つintへの暗黙のキャストです。!演算子を2回呼び出すと、奇妙な場合でも機能するboolに変換されます。


0

それは正しいですが、Cではここでは意味がありません。'if 'と' && 'は' !! 'がない場合と同じように式を扱います。

これをC ++で行う理由は、 '&&'がオーバーロードされる可能性があるためだと思います。しかし、そうすれば '!'になる可能性があるので、との型のコードを調べずにブール値を取得することを実際に保証するものではありませ。たぶん、C ++の経験がある人なら説明できるでしょう。おそらくこれは、多層防御の一種であり、保証ではありません。variableapi.call


コンパイラは、iforのオペランドとしてのみ使用されている場合はどちらの方法でも値を同じように扱いますが、に置き換えると、一部のコンパイラで&&使用!!すると役立つ場合if (!!(number & mask))がありbit triggered = !!(number & mask); if (triggered)ます。ビット型の一部の組み込みコンパイラでは、たとえばビット型に256を割り当てるとゼロが生成されます。がなければ、!!明らかに安全な変換(if条件を変数にコピーしてから分岐する)は安全ではありません。
スーパーキャット2014

0

多分プログラマーはこのようなことを考えていました...

!! myAnswerはブール値です。コンテキストではブール値になるはずですが、確認するために何かをバンバンするのが大好きです。たまに私にかみついた不思議なバグがあり、バンバンしたので、私はそれを殺しました。


0

これはダブルバントリックの例である可能性があります。詳細については、The Safe Bool Idiomを参照してください。ここでは、記事の最初のページを要約します。

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

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)は、一部の人々がダブルバントリックと呼ぶものです。


1
C ++ 11以降では、explicit operator bool他の整数型への暗黙的な変換を防ぐために使用できます。
Arne Vogel
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.