web-dev-qa-db-ja.com

c ++にブール値の&& =または|| =がないのはなぜですか?

発生する可能性のある "非常に悪いこと" があります&& =および|| =がbool foo = foo && barおよびbool foo = foo || barの構文糖として使用されましたか?

111
Kache

boolは、C++ではtrueまたはfalseのみです。そのため、&=|=を使用することは比較的安全です(表記法は特に好きではありませんが)。確かに、論理演算ではなくビット演算を実行します(したがって、短絡しません)が、これらのビット演算は、明確に定義されたマッピングに従います。これは、論理演算両方のオペランドのタイプがboolである限り。1

ここで他の人が言ったことに反して、C++のboolは、2などの異なる値を持つことはできません。その値をboolに割り当てると、標準に従ってtrueに変換されます。

無効な値をboolに取得する唯一の方法は、ポインターでreinterpret_castを使用することです。

int i = 2;
bool b = *reinterpret_cast<bool*>(&i);
b |= true; // MAY yield 3 (but doesn’t on my PC!)

ただし、このコードでは未定義の動作が発生するため、C++コードを適合させる際のこの潜在的な問題は無視しても問題ありません。


1 Angewのコメントが示すように、確かにこれはかなり大きな警告です。

bool b = true;
b &= 2; // yields `false`.

理由は、b & 2は整数昇格を実行するため、式はstatic_cast<int>(b) & 2と同等になり、結果は0になり、その後boolに変換されます。したがって、operator &&=が存在すると型の安全性が向上するのは事実です。

68
Konrad Rudolph

&&&のセマンティクスは異なります。&&は、最初のオペランドがfalseの場合、2番目のオペランドを評価しません。つまり

flag = (ptr != NULL) && (ptr->member > 3);

安全ですが、

flag = (ptr != NULL) & (ptr->member > 3);

両方のオペランドはbool型です。

&=|=についても同様です:

flag = CheckFileExists();
flag = flag && CheckFileReadable();
flag = flag && CheckFileContents();

以下とは異なる動作をします:

flag = CheckFileExists();
flag &= CheckFileReadable();
flag &= CheckFileContents();
43
Niki

短い答え

すべての演算子+=-=*=/=&=|=...は算術演算であり、同じ期待値を提供します。

x &= foo()  // We expect foo() be called whatever the value of x

ただし、演​​算子&&=および||=は論理的であり、多くの開発者はfoo()が常にx &&= foo()で呼び出されることを期待するため、これらの演算子はエラーを起こしやすくなります。

bool x;
// ...
x &&= foo();           // Many developers might be confused
x = x && foo();        // Still confusing but correct
x = x ? foo() : x;     // Understandable
x = x ? foo() : false; // Understandable
if (x) x = foo();      // Obvious
  • x = x && foo()のショートカットを取得するには、C/C++をさらに複雑にする必要がありますか?

  • 暗号文x = x && foo()をさらに難読化したいですか?
    それとも、if (x) x = foo();のような意味のあるコードを書きたいですか?


長い答え

&&=の例

&&=演算子が使用可能な場合、次のコード:

bool ok = true; //becomes false when at least a function returns false
ok &&= f1();
ok &&= f2(); //we may expect f2() is called whatever the f1() returned value

以下と同等です:

bool ok = true;
if (ok) ok = f1();
if (ok) ok = f2(); //f2() is called only when f1() returns true

この最初のコードはerror-proneです。多くの開発者は、f2()が返す値に関係なく、常にf1()が呼び出されると考えるからです。 bool ok = f1() && f2();trueを返す場合にのみf2()が呼び出されるf1()を書くようなものです。

  • 開発者が実際にf2()trueを返す場合にのみf1()を呼び出したい場合、上記の2番目のコードはエラーを起こしにくいです。
  • そうでない場合(開発者はf2()を常に呼び出すことを望んでいます)、&=で十分です:

&=の例

bool ok = true;
ok &= f1();
ok &= f2(); //f2() always called whatever the f1() returned value

さらに、コンパイラは、上記のコードを以下のコードよりも最適化する方が簡単です。

bool ok = true;
if (!f1())  ok = false;
if (!f2())  ok = false;  //f2() always called

&&&を比較

&&&演算子がbool値に適用されたときに同じ結果を与えるかどうか疑問に思うかもしれません。

次のC++コードを使用して確認しましょう。

#include <iostream>

void test (int testnumber, bool a, bool b)
{
   std::cout << testnumber <<") a="<< a <<" and b="<< b <<"\n"
                "a && b = "<< (a && b)  <<"\n"
                "a &  b = "<< (a &  b)  <<"\n"
                "======================"  "\n";
}

int main ()
{
    test (1, true,  true);
    test (2, true,  false);
    test (3, false, false);
    test (4, false, true);
}

出力:

1) a=1 and b=1
a && b = 1
a &  b = 1
======================
2) a=1 and b=0
a && b = 0
a &  b = 0
======================
3) a=0 and b=0
a && b = 0
a &  b = 0
======================
4) a=0 and b=1
a && b = 0
a &  b = 0
======================

結論

したがって、[〜#〜] yes [〜#〜]&&&に置き換えて、bool値を;;)
したがって、&=の代わりに&&=を使用することをお勧めします。
&&=はブール値としては役に立たないと考えることができます。

||=にも同じ

演算子|=も小さいerror-prone||=より少ない

開発者が、f2()が次の代わりにfalseを返す場合にのみf1()を呼び出す場合

bool ok = false;
ok ||= f1();
ok ||= f2(); //f2() is called only when f1() returns false
ok ||= f3(); //f3() is called only when f1() or f2() return false
ok ||= f4(); //f4() is called only when ...

次のより理解しやすい代替案をアドバイスします。

bool ok = false;
if (!ok) ok = f1();
if (!ok) ok = f2();
if (!ok) ok = f3();
if (!ok) ok = f4();
// no comment required here (code is enough understandable)

または、すべてを1行スタイルにしたい場合:

// this comment is required to explain to developers that 
// f2() is called only when f1() returns false, and so on...
bool ok = f1() || f2() || f3() || f4();
24
olibre