&&= 和 ||= 运算符

19

可能是重复的问题:
为什么Java没有条件与和条件或运算符的复合赋值版本?(&&=, ||=)
为什么不存在“&&=”运算符?

今天在工作中,我写了以下代码(b和b1的真实身份是保密的 :)

b &&= b1; // meaning b = b && b1; 

我盯着它看了几秒钟,意识到没有这样的运算符。为了确保,我点击了编译,但失败了。为了更加确定,我查阅了标准。

为什么没有这样的运算符?我可以想到一些原因:

  1. b &&= b1b = b && b1 可能不等价,因为&&有短路求值。
  2. &&= 很丑陋。
  3. &&= 很少使用。

我并不认为拥有这样的运算符非常有用。我也不认为以上三个理由中的任何一个或全部都不足以制止创建该运算符。我的问题是:是否存在我忽略的更严重的原因?


2
b &= isSomethingWithSideEffects(); 会运行函数,而如果它被改成 &&= ,假设 b 是 false 的话,那么函数就不会被运行了,对吗? - Dusty
1
抱歉,我不明白为什么Dingo的评论被点赞了。有人能解释一下吗? - John
2
@John - 这是一个反例,用来反驳现已删除的建议,即 &= 等同于 &&=(正如我的评论所述)。 - Dusty
1
忘了那个问题 - 我非常想知道bb1的真实身份秘密会赋予我怎样无法估量的力量! - Michael Burr
2
@关闭投票者:请考虑不要关闭此问题,因为潜在的重复引用都是关于Java的,而答案是Java中没有&&=,因为C中也没有。这些线程都没有提供令人满意的答案,我希望在这里得到答案。请考虑一下。谢谢。 - Armen Tsirunyan
显示剩余7条评论
6个回答

16

我不知道为什么这个问题以及一些回答提到相应逻辑运算符的短路行为可能会出现问题。

&&=||= 运算符与短路没有任何相关问题。它们应该与 += 和其他类似的运算符统一定义,也就是说,a &&= b 应等价于 a = a && b,但在 &&= 版本中只对 a 求值一次。这意味着如果 a 最初为零,则 b 根本不会被求值。很容易理解。

因此,它们在语言中不存在的唯一原因就是“只是因为”。


3
没错。Perl有这些运算符,并且拥有这种行为;虽然我听到很多对Perl的批评,但从未听说过有人抱怨&&=难以理解。 - Porculus
3
是的,我真的希望有这样的运算符。 &&|| 运算符已经与其他二元运算符(例如+)有着显著不同的行为,我们一直都能很好地应对。 我认为 &&=||= 也可以有与其他赋值形式显著不同的行为,即使它涉及 RHS 的可选评估,也没有任何问题。 - AnT stands with Russia
1
@T.E.D.:在 a && b; 中,b也不会被计算。 - user unknown
@Armen Tsirunyan:你能展示一个例子,在这个例子中a = a && b;中的a具有副作用,是可分配的,并且可以成为LHS吗?我担心我无法理解这个论点。 - user unknown
很抱歉打扰到 C++ 初学者,但是在 f() = f() && false; 这个赋值语句中,它有任何作用吗?为什么要写这样的东西而不是 f(), f() && false;?虽然可能编写这样的语句,但是这个赋值语句在这样的语句中有任何含义吗? - user unknown
显示剩余7条评论

9

我以前也想过使用它们。我怀疑丑陋本身并不是决定因素,短路行为是迄今为止我听到的最好的反对它们的论据之一。

另一个 contributing factor 是 C 语言被设计为接近底层操作,几乎所有的运算符都直接对应主要架构中的指令。我认为在 x86、PPC 等架构中没有一个指令可以直接实现 b &&= b1;


1
+1. PDP-11 上可用的操作可能更具决定性,但我仍然认为你有点眼光。 - T.E.D.
1
什么?短路是支持它们的论点。如果没有这个,只需在布尔值中使用&=即可。至于指令,与b && ( b = b1 );相同(当然只评估一次b)。编译器在分支和按位AND之间进行选择。 - Potatoswatter
@Potatoswatter:反对的观点是它们将是唯一可以短路的赋值运算符。而你的小片段没有必要的行为,它需要是if (b) b = (b1 != 0);。与运算符&&不同,你的版本可能会产生除0或1之外的值。 - Ben Voigt
@Ben:我只是试图说明编译器如何进行短路计算。b && ( b = (bool) b1 )似乎等同于所需的行为,但这并不重要。它们将是唯一的赋值运算符来进行短路计算...与唯一的非赋值二元运算符相对应。 - Potatoswatter
@Ben:对,我搞反了。不过这并不影响什么。 - Potatoswatter
显示剩余8条评论

2
运算符不存在的最大原因可能是K&R没有想到任何吸引人的定义方式。我有时也希望有一个 ->= 运算符 (ptr->=next 相当于 ptr = ptr->whatever)。
我认为 &&= 的问题在于不清楚以下哪个是最有用的,或者它应该是什么:
  if (lhs && rhs) lhs = 1; else lhs = 0;
  if (!rhs) lhs = 0; else lhs = !(!lhs));
  if (lhs && !rhs) lhs = 0;
  if (!rhs) lhs = 0;
第一种变化是语法最明显的建议,但从实际角度来看,如果两个术语都不为零,则保持左侧不变通常比将其设置为“1”更有用。
BTW,我经常希望有一个逗号运算符的变体,它将评估左侧,保存值,然后评估右侧,并返回左侧的值。相当于:
int foo(int p1, int p2) return p1;
除了适用于任何类型 (p2 不需要与 p1 相同类型,也可以是 void),并且具有保证的从左到右的评估顺序。对于像 arr[ptr ~, ptr+=2]; 这样的非单位步长的后增量索引或某些类型的数据交换操作(如 var1 = (var2 ~, var2=var1); 等),非常方便。

4
在C语言中,所有复合赋值运算符(op=)的定义形式为lhs = lhs op rhs,其中lhs表达式只会被评估一次。因此,我认为&&=的定义几乎没有歧义(与其他复合赋值运算符相同)。 - Michael Burr
@Michael Burr:从语法上讲,这将是自然的实现方式,但在实践中,第三种方法比第一种更有用的情况时有发生,但我想不出第一种更有用的情况有多少。 - supercat
1
我曾提出了一个与现已删除的问题等价的方案:if (lhs) lhs = (rhs != 0);。它似乎具有预期的短路行为。然而,执行 if (lhs) lhs = rhs; 可能更快,它也会短路但不会将所有非零的 rhs 强制转换为 1。 - Ben Voigt

2
因为 a && b 的结果始终是 0 或者 1,所以我认为只有 C99 中的 _Bool 类型才能对其进行明确解释。由于在 C 语言被创建时该类型并不存在,所以这个操作符也没有被包含进来。而现在很少有人会轻易地向 C 添加另一个操作符,因为这将对所有现有的解析器产生影响。

1

就我个人而言,我会选择你最初提出的第一个理由。布尔运算符具有短路语义,如果将其转换为赋值运算符,则可能会导致一些非常棘手的情况。要么您不再使它们短路,要么您创建一种奇怪的“可选”赋值运算符(仅在左侧值已经非零时才执行右侧操作并分配结果)。无论哪种方式,都会因为人们期望其他行为而创建微妙的错误。


0

编辑:语言错误,但仍然适用

我同意你的三个理由,尽管有一种情况下我曾经为缺少这个运算符而感到遗憾,那就是在编写自定义反序列化程序时。在一些情况下,当一个未正确序列化的对象并不真正“异常”(并且为了防止C#中非常常见的失败引起的异常开销),我会使用布尔返回值来表示反序列化操作是否成功。

这段代码完全是理论上的,Nested1、Nested2和Nested3都是结构体:

public struct ComplexStruct
{
    private Nested1 nested1;
    private Nested2 nested2;
    private Nested3[] nested3;

    public bool Read(Reader reader)
    {
       bool ret = true;
       int nested3Length = 0;

       ret &&= nested1.Read(reader);
       ret &&= nested2.Read(reader);
       ret &&= reader.ReadInt32(ref nested3Length);

       for(int i = 0; (ret && i < nested3Length); i++)
       {
          ret &&= nested3[i].Read(reader);
       }

       return ret;
    }
}

好的,当我需要循环部分时,情况几乎相同。我现在会编辑我的问题以澄清。 - Armen Tsirunyan
在这种情况下,你可以尽早地使用 return false; - Ben Voigt
很酷...在我的情况下,我想要短路行为,所以我最终在大多数情况下使用ret = ret && xxx...它有点啰嗦,但至少清楚地表明我正在进行短路布尔比较。 - LorenVS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接