web-dev-qa-db-ja.com

C ++コードでの二重否定

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

私は主にC++を扱っており、彼らが書くコードの多くは、ブール論理に二重否定を使用しています。

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

私はこれらの人たちが知的なプログラマーであることを知っています、彼らが偶然にこれをしていないことは明らかです。

私は経験豊富なC++の専門家ではありません。彼らがこれを行っている理由は、評価される値が実際のブール表現であることを絶対に肯定的にしたいからです。したがって、彼らはそれを否定し、それを再び否定して、実際のブール値に戻します。

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

114

Boolに変換するのはコツです。

114
Don Neufeld

実際には、いくつかのコンテキストでは非常に便利なイディオムです。これらのマクロを使用します(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++の両方で機能することを行います。

68
Tom Barta

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

50
fizzer

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

11
jwfearn

これは、書き込み(変数!= 0)を回避する手法です。つまり、どの型からでもブールに変換します。

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

コードは読みやすいものでなければなりません。そうでなければ、不必要に複雑なものを理解するのに時間がかかるので、将来のために時間の借金を残します。

11

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

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

「bar」行は、MSVC++で「boolに「true」または「false」(パフォーマンス警告)を強制する値」を生成しますが、「baz」行はうまくいきます。

9
RobH

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

5
Marcin

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

この問題を回避するために、一部の開発者は次のブールIDを使用します。_!num_value_は_bool true_を返しますif _num_value == 0_;それ以外の場合はfalse。 _!!num_value_は_bool false_を返しますif _num_value == 0_;それ以外の場合はtrue。 _num_value_をboolに変換するには、単一の否定で十分です。ただし、ブール式の元の意味を復元するには、二重否定が必要です。

このパターンはidiomとして知られています。つまり、この言語に精通している人々が一般的に使用するものです。したがって、static_cast<bool>(num_value)ほど、アンチパターンとは見なしません。キャストによって正しい結果が得られる場合がありますが、一部のコンパイラーはパフォーマンス警告を出力するため、引き続き対処する必要があります。

これに対処する別の方法は、_(num_value != FALSE)_と言うことです。私もそれで問題ありませんが、全体として、_!!num_value_は冗長性がはるかに低く、より明確になる可能性があり、2回目にそれを混乱させることはありません。

2
KarlU

_!!_は、ブール型を持たない元のC++に対処するために使用されました(Cもそうではありませんでした)。


問題の例:

if(condition)内では、conditionは_double, int, void*_などのような型に評価する必要がありますが、boolはまだ存在しないため評価する必要はありません。

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

_int256 x = foo();
if (x) ...
_

xが「true」またはゼロ以外であったかどうかをテストするには、if (x)xを整数に変換し、thenそのintがゼロでないかどうかを評価します。 _(int) x_の典型的なオーバーロードは、xのLSビットのみを返します。 if (x)は、xのLSbitのみをテストしていました。

ただし、C++には_!_演算子があります。オーバーロードされた_!x_は通常、xのすべてのビットを評価します。したがって、非反転ロジックに戻るには、if (!!x)が使用されます。

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

1
chux

Marcin で述べたように、演算子のオーバーロードが機能するかどうかは重要かもしれません。それ以外の場合、C/C++では、次のいずれかを実行している場合を除いて重要ではありません。

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

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

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

大きなブール式のコンテキストでは、単純に物事を混乱させると思います。

1
Michael Burr

変数がオブジェクトタイプの場合、!演算子は定義されていますが、boolへのキャストはありません(または、さらに悪い意味でintへの暗黙のキャストです。

1
Joshua

これはdouble-bang trickの例です。詳細については 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_などの無意味な操作を許可するため、安全ではないと見なされます。

暗黙的な変換またはオーバーロードの問題を回避するため、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)は、一部の人々がdouble-bang trickと呼ぶものです。

0
sam

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

C++でこれを行う理由は、「&&」がオーバーロードされる可能性があるためだと思います。しかし、そうすると '!'になる可能性があるため、reallyは、variableおよびapi.call。 C++の経験が豊富な人が説明できるかもしれません。おそらく、それは保証ではなく、多層防御の手段として意図されています。

0
Darius Bacon

たぶんプログラマーはこのようなことを考えていた...

!! myAnswerはブール値です。コンテキストでは、それはブール値になるはずですが、念のために物事をバタンと鳴らすのが大好きです。

0
dongilmore