【问题标题】:Why doesn't c++ have &&= or ||= for booleans?为什么 c++ 没有 &&= 或 ||= 用于布尔值?
【发布时间】:2023-03-26 21:13:01
【问题描述】:

是否有可能发生的"very bad thing" &&=||= 被用作 bool foo = foo && barbool foo = foo || bar 的语法糖?

【问题讨论】:

  • 查看另一个问题:stackoverflow.com/questions/2324549/… 那是关于 Java 的,但共享 C 血统,相同的论点大多适用。
  • 基本上,只是 c++ 没有它 b/c 他们没有把它放进去 - 像 Ruby 这样的语言有它。嘘……
  • 但是在 Ruby 中,对于任何类型,x ||= y 是否大致等同于 C++ x = x ? x : y;?换句话说,“如果尚未设置,请设置为 y”。这比 C 或 C++ x ||= y 有用得多,后者(禁止运算符重载)会“将 x 设置为 (bool)y,除非已经设置”。我并不急于为此添加另一个运营商,这似乎有点弱。只需写if (!x) x = (bool)y。但是,我并没有真正使用足够的 bool 变量来需要额外的运算符,这些运算符只对那一种类型真正有用。
  • 我确信 C++ 没有 &&=||= 的主要原因仅仅是 C 没有它们。我有理由确定 C 没有它们的原因是该功能被认为不够有用。
  • 此外,符号bool foo = foo || bar; 会引发未定义的行为,因为foo 在评估foo || bar 之前没有初始化。当然,这应该是类似bool foo = …initialization…; …; foo = foo || bar; 这样的问题,然后这个问题就成立了。

标签: c++ boolean-operations


【解决方案1】:

bool 在 C++ 中只能是 truefalse。因此,使用&=|= 是相对安全的(尽管我不是特别喜欢这种表示法)。诚然,它们将执行位操作而不是逻辑操作(因此它们不会短路),但这些位操作遵循定义明确的映射,这实际上等同于逻辑操作,只要因为两个操作数的类型都是bool1

与其他人在这里所说的相反,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 &amp; 2 执行整数提升,使得表达式随后等价于static_cast&lt;int&gt;(b) &amp; 2,从而产生0,然后将其转换回bool。所以operator &amp;&amp;= 的存在确实会提高类型安全性。

【讨论】:

  • 但是 && 和 ||运算符将处理任何 转换 为 bool 的东西,而不仅仅是 bool。
  • 他们不会做同样的事情,即使在布尔值上也是如此。 ||&amp;&amp; 快捷方式,即如果第一个操作数是 true,则第二个参数不是操作数(对于 &amp;&amp; 分别是 false)。 |&amp;|=&amp;= 总是计算两个操作数。
  • 这并没有回答为什么 &&= 和 ||= 不是 c++ 运算符的问题。
  • &amp;= 用于bool 类型的左侧是安全的,因为右侧完全有可能是其他类型比bool (例如islower 或另一个返回非零为真值的C stdlib 函数)。如果我们有假设的&amp;&amp;=,它可能会强制右侧转换为bool,而&amp;= 不会。换句话说,bool b = true; b &amp;= 2; 的结果是 b == false
  • @Antonio 艰难的人群。 ?
【解决方案2】:

&amp;&amp;&amp; 具有不同的语义:如果第一个操作数是 false&amp;&amp; 将不会计算第二个操作数。即类似

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

是安全的,但是

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

不是,尽管两个操作数都是bool 类型。

&amp;=|= 也是如此:

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

会表现得不同于:

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

【讨论】:

  • 在我看来更有理由拥有 &&=。 =P
  • 但这并不是真正的答案。
【解决方案3】:

简答

所有运算符+=-=*=/=&amp;=|=...都是算术运算符并提供相同的期望:

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

但是,运算符 &amp;&amp;=||= 是合乎逻辑的,并且这些运算符可能容易出错,因为许多开发人员希望 foo() 总是在 x &amp;&amp;= 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
  • 我们真的需要让 C/C++ 变得更加复杂才能获得x = x &amp;&amp; foo() 的快捷方式吗?

  • 我们真的想要更多地混淆神秘的声明x = x &amp;&amp; foo()吗?
    还是我们想写像if (x) x = foo();这样有意义的代码?


长答案

&amp;&amp;= 的示例

如果&amp;&amp;= 运算符可用,则此代码:

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

第一个代码容易出错,因为许多开发人员会认为无论f1() 返回值如何,总是调用f2()。这就像写bool ok = f1() &amp;&amp; f2();,其中f2() 仅在f1() 返回true 时被调用。

  • 如果开发者真的希望f2() 仅在f1() 返回true 时被调用,那么上面的第二个代码更不容易出错。
  • 否则(开发人员希望始终调用f2()),&amp;= 就足够了:

&amp;= 的示例

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

比较 &amp;&amp;&amp;

我们可能想知道运算符&amp;&amp;&amp; 在应用于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 我们可以将 &amp;&amp; 替换为 &amp; 以获得 bool 值 ;-)
所以最好使用&amp;= 而不是&amp;&amp;=
我们可以认为&amp;&amp;= 对布尔值没有用处。

||= 相同

操作员|= 也比||= 更不容易出错

如果开发人员希望仅在f1() 返回false 时调用f2(),而不是:

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)

或者如果你更喜欢一行风格:

// 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();

【讨论】:

  • 如果我真的想要这种行为怎么办?如果左手表达式错误,则不会执行右手表达式。两次写变量很烦人,比如success = success &amp;&amp; DoImportantStuff()
  • 我的建议是写if(success) success = DoImportantStuff()。如果声明success &amp;&amp;= DoImportantStuff() 被允许,许多开发人员会认为DoImportantStuff() 总是被称为success 的任何值。希望这能回答您的疑问……我还改进了答案的许多部分。请告诉我我的答案现在是否更容易理解? (关于您的评论目的)干杯,再见 ;-)
  • “如果声明 success &amp;&amp;= DoImportantStuff() 被允许,许多开发人员会认为 DoImportantStuff() 总是被称为无论成功的价值如何。”你可以说if (success &amp;&amp; DoImportantStuff())。只要他们记得 if 语法背后的逻辑,他们就应该不会遇到&amp;&amp;= 的问题。
  • 我不明白人们怎么会认为 f1() 总是在 ok &amp;&amp;= f(1) 中计算,但不会认为它总是在 ok = ok &amp;&amp; f(1) 中计算。对我来说似乎同样可能。
  • 我实际上希望 v1 += e2 是变量 v1 和表达式 e2 的 v1 = v1 + e1 的语法糖等价物。只是一个简写符号,仅此而已。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
  • 1970-01-01
  • 2020-02-01
  • 1970-01-01
  • 2011-02-26
  • 1970-01-01
相关资源
最近更新 更多