発生する可能性のある "非常に悪いこと" があります&& =および|| =がbool foo = foo && bar
およびbool foo = foo || bar
の構文糖として使用されましたか?
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 &&=
が存在すると型の安全性が向上するのは事実です。
&&
と&
のセマンティクスは異なります。&&
は、最初のオペランドが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();
すべての演算子+=
、-=
、*=
、/=
、&=
、|=
...は算術演算であり、同じ期待値を提供します。
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();