web-dev-qa-db-ja.com

なぜ使用する!! intをboolに変換するときは?

この方法で整数をブール値に変換する理由は何ですか?

bool booleanValue = !!integerValue;

ただの代わりに

bool booleanValue = integerValue;

私が知っているのは、VC++ 7では後者が C4800警告 を引き起こし、前者はそうではないということです。 2つの間に他の違いはありますか?

76
sharptooth

「!!」の問題イディオムは、簡潔で、見づらく、タイプミスと間違えやすく、「!」のいずれかを簡単に削除できるなどです。 「C/C++でどれだけ可愛く見えるか」カテゴリに入れました。

bool isNonZero = (integerValue != 0);と書くだけで...明確にしてください。

111
user143506

歴史的に、!!イディオムは、CとC++が真のbool型を持っていなかったため、boolのような変数で期待される2つの値の1つがブールに実際に含まれていることを確認するために使用されましたintsを使用します。これは、「実際の」boolsの問題ではなくなりました。

しかし、!!は(コンパイラとコードで作業する将来の人々の両方にとって)文書化の効率的な手段であり、そうです、あなたは本当にintboolにキャストするつもりでした。

49
T.J. Crowder

これは、C言語(および一部の先行標準C++コンパイラも)にbool型がなく、intだけであったために使用されます。したがって、論理値を表すためにintsが使用されました。0falseを意味し、他のすべてはtrueでした。 !演算子は、1から0を返し、他のすべてから0を返していました。ダブル!を使用してこれらを反転し、その値が論理値に応じて0または1だけであることを確認するために使用しました。

C++では、適切なbool型を導入するため、これを行う必要はもうありません。ただし、すべてのレガシーソースを更新することはできません。CのC++との下位互換性のため(ほとんどの場合)、更新する必要はありません。しかし、同じ理由から、多くの人々がまだそれを行っています:boolsをまだ理解していない古いコンパイラとの後方互換性を保つためです。

そして、これが唯一の本当の答えです。他の答えは誤解を招くものです。

14
SasQ

!integerValueはintegerValue == 0を意味し、!! integerValueはintegerValue!= 0を意味するため、boolを返す有効な式です。後者は情報の損失を伴うキャストです。

13
StampedeXV

別のオプションは、アセンブリコードを1行少なく生成するように見える三項演算子です(とにかくVisual Studio 2005で)。

bool ternary_test = ( int_val == 0 ) ? false : true;

アセンブリコードを生成します。

cmp DWORD PTR _int_val$[ebp], 0
setne   al
mov BYTE PTR _ternary_test$[ebp], al

対:

bool not_equal_test = ( int_val != 0 );

生成するもの:

xor eax, eax
cmp DWORD PTR _int_val$[ebp], 0
setne   al
mov BYTE PTR _not_equal_test$[ebp], al

大きな違いではないことはわかっていますが、それについて興味があり、自分の発見を共有すると思っただけです。

6
Brian

Boolには、0と1の2つの状態しか設定できません。整数には、-2147483648〜2147483647の任意の状態を指定でき、符号付き32ビット整数を想定しています。単項!演算子は、入力が0の場合1を出力し、入力が0以外の場合0を出力します。したがって、!0 = 1および!234 = 0です。 0が1に、1が0になるように出力を切り替えるだけです。

したがって、最初のステートメントは、booleanValueが0または1に設定され、他の値は設定されないことを保証しますが、2番目のステートメントはそうではありません。

5
John Scipione

!!boolに変換する慣用的な方法であり、そのような変換の非効率性の疑いに関するVisual C++コンパイラの愚かな警告を止める働きをします。

他の回答とコメントから、多くの人がWindowsプログラミングにおけるこのイディオムの有用性に慣れていないことがわかります。つまり、彼らは本格的なWindowsプログラミングを行っていないということです。そして、彼らが遭遇したものが代表的なものであると盲目的に仮定します(そうではありません)。

#include <iostream>
using namespace std;

int main( int argc, char* argv[] )
{
    bool const b = static_cast< bool >( argc );
    (void) argv;
    (void) b;
}
> [d:\ dev\test] 
> cl foo.cpp
 foo.cpp 
 foo.cpp(6):警告C4800: 'int':値をboolに強制する 'true'または 'false'(パフォーマンス警告)
 
 [d:\ dev\test] 
> _ 

そして、少なくとも1人は、まったくの初心者がその意味を認識しない場合、それは良くないと考えています。まあそれは愚かです。まったくの初心者が認識も理解もしていないことはたくさんあります。まったくの初心者が理解できるようにコードを書くことは、専門家にとっては何でもありません。学生でもない。初心者を認識しない演算子と演算子の組み合わせを除外するパスから始めます...まあ、そのアプローチに適切な説明を与える言葉がありません、申し訳ありません。

乾杯

User143506の答えは正しいですが、パフォーマンスの問題の可能性があるため、asmの可能性を比較しました:

_return x;_、_return x != 0;_、_return !!x;_およびreturn boolean_cast<bool>(x)でさえ、asm命令のこの完璧なセットになります。

_test    edi/ecx, edi/ecx
setne   al
ret
_

これは、GCC 7.1およびMSVC 19 2017でテストされました。(MSVC 19 2017のboolean_converterのみがより多くのasm-codeになりますが、これはテンプレート化と構造によって引き起こされ、パフォーマンスの観点から無視できます。上記の行は、同じランタイムで異なる機能のために複製される場合があります。

つまり、パフォーマンスの違いはありません。

PS:このboolean_castが使用されました:

_#define BOOL int
// primary template
template< class TargetT, class SourceT >
struct boolean_converter;

// full specialization
template< >
struct boolean_converter<bool, BOOL>
{
  static bool convert(BOOL b)
  {
    return b ? true : false;
  }
};

// Type your code here, or load an example.
template< class TargetT, class SourceT >
TargetT boolean_cast(SourceT b)
{
  typedef boolean_converter<TargetT, SourceT> converter_t;
  return converter_t::convert(b);
}

bool is_non_zero(int x) {
   return boolean_cast< bool >(x);
}
_
1
kallitokaco