重载运算符 |? 的求值顺序

发布于 2024-12-01 10:51:20 字数 800 浏览 3 评论 0原文

标准中的 5.15 逻辑 OR 运算符表示如下:

与|、||不同保证从左到右评估;

这是否意味着我无法在标准中找到 | 被定义为从右到左评估,或者它是实现定义的?当操作员过载时,这会有所不同吗?我编写了一个快速程序来测试这一点,MSVC++ 和 GCC 似乎都是从右到左计算的。

#include<iostream>
using namespace std;

int foo = 7;

class Bar {
public:
    Bar& operator|(Bar& other) {
        return *this;
    }
    Bar& operator++() {
        foo += 2;
        return *this;
    }
    Bar& operator--() {
        foo *= 2;
        return *this;
    }
};

int main(int argc, char** argv) {
    Bar a;
    Bar b;
    Bar c = ++a | --b;
    cout << foo;
}

这会输出 16。 如果 ++a--b 交换,则输出 19

我还考虑到我可能会遇到序列点规则之间的多个更改(以及因此未定义的行为),但我不确定如何/是否适用于两个单独的实例作为操作数。

5.15 Logical OR operator in the standard says the following:

Unlike |, || guarantees left-to-right evaluation;

Does this mean somewhere I cannot locate in the standard, | is defined to evaluate right-to-left, or that it is implementation-defined? Does this vary when the operator is overloaded? I wrote a quick program to test this and both MSVC++ and GCC seem to evaluate right-to-left.

#include<iostream>
using namespace std;

int foo = 7;

class Bar {
public:
    Bar& operator|(Bar& other) {
        return *this;
    }
    Bar& operator++() {
        foo += 2;
        return *this;
    }
    Bar& operator--() {
        foo *= 2;
        return *this;
    }
};

int main(int argc, char** argv) {
    Bar a;
    Bar b;
    Bar c = ++a | --b;
    cout << foo;
}

This outputs 16.
If ++a and --b are switched it outputs 19.

I've also considered that I may be running into the multiple changes between sequence points rule (and thus undefined behavior), but I'm unsure how/if that applies with two separate instances as operands.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦忆晨望 2024-12-08 10:51:20

现在忽略该运算符,只需注意这一点:

(x + y) * (z + 1)

在这里,在进行乘法之前必须对两个操作数进行求值(否则我们不知道要乘什么)。在 C++ 中,执行此操作的顺序未指定:可以先是 (x + y),也可以是 (z + 1)首先,编译器认为更好。†

对于运算符 | 也是如此。但是,运算符|| 必须短路,并且为了做到这一点,它必须严格从左到右进行计算。 (如果左边的计算结果为 true,则计算结束,而不计算右边的操作数。)这就是这句话的意思。

†请注意,它可能没有任何偏好,只是按照列出的顺序进行评估。这就是为什么你会得到你所做的输出,尽管你不能在语言级别依赖它。

Ignore that operator for now, and just take note of this:

(x + y) * (z + 1)

Here, both operands must be evaluated before the multiplication can take place (otherwise we wouldn't know what to multiply). In C++, the order in which this is done is unspecified: it could be (x + y) first, or (z + 1) first, whatever the compiler feels is better.†

The same is true for the operator |. However, operator || must short-circuit, and in order to do that, it must evaluate strictly left to right. (And if the left evaluation yields true, the evaluation ends without evaluating the right operand.) That's what the sentence means.

†Note that it may have no preference one way or another, and just evaluate in the order it's listed. This is why you get the output you do, though you cannot rely on it at the language level.

つ可否回来 2024-12-08 10:51:20

正如其他人所说,这意味着双方的评价顺序不明确。回答您的其他问题 -

我还考虑到我可能会遇到序列点规则之间的多次更改(以及因此未定义的行为)

不,您的情况不会修改两个相邻序列点之间的 foo 。在进入函数之前和离开函数之前,总是存在一个序列点,这意味着 foo 的两次修改都发生在两对不同的序列点之间。

当运算符重载时,这会有所不同吗?

第 5 条全部只讨论内置运算符。对于用户定义的运算符实现,这些规则不适用。对于 || 也是如此,对于用户定义的运算符,未指定顺序。但请注意,它仅适用于用户定义的运算符;当两个操作数都转换为 bool 并触发内置运算符时则不会:

struct A { 
  operator bool() const { return false; }
};

struct B {
  operator bool() const { return true; }
};

int main() {
  A a;
  B b;
  a || b;

  shared_ptr<myclass> p = ...;
  if(p && p->dosomething()) ...;
}

这将始终首先执行 A::operator bool,然后执行 B::operator bool。如果 p 的计算结果为 true,它只会调用 p->dosomething()

As others said, it means that the order of the evaluation of the two sides is unspecified. To answer your other questions -

I've also considered that I may be running into the multiple changes between sequence points rule (and thus undefined behavior)

No, your case does not modify foo in between two adjacent sequence points. Before entering a function and before leaving a function, there always is a sequence point, which means that both modifications of foo happen in between two different pairs of sequence points.

Does this vary when the operator is overloaded?

All of clause 5 only talks about builtin operators. For user defined operator implementations, the rules don't apply. So also for ||, for user defined operators the order is not specified. But notice that it is only for user defined operators; not when both operands are converted to bool and trigger the builtin operator:

struct A { 
  operator bool() const { return false; }
};

struct B {
  operator bool() const { return true; }
};

int main() {
  A a;
  B b;
  a || b;

  shared_ptr<myclass> p = ...;
  if(p && p->dosomething()) ...;
}

This will always first execute A::operator bool, and then B::operator bool. And it will only call p->dosomething() if p evaluates to true.

单调的奢华 2024-12-08 10:51:20

这是否意味着我无法在标准中找到某个地方,|被定义为从右到左计算,或者它是实现定义的?

迂腐地说,| 运算符的参数求值顺序是未指定的。这意味着操作数可以按任意顺序求值。

然而,逻辑运算符(即&&|| 等)和逗号运算符的操作数的求值顺序是指定的,即从左到右。

Does this mean somewhere I cannot locate in the standard, | is defined to evaluate right-to-left, or that it is implementation-defined?

Pedantically speaking the order of evaluation of arguments of | operator is unspecified. So that means the operands can be evaluated in either order.

However the order of evaluation of operands of logical operators (i.e &&, || etc) and comma operator is specified i.e from left to right.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文