gpt4 book ai didi

c++ - 使用 C++17 排序的链式复合赋值仍然是未定义的行为?

转载 作者:行者123 更新时间:2023-12-03 06:49:21 24 4
gpt4 key购买 nike

本来,我提出了一个更复杂的例子,这个是@n提出的。 '代词' m.在现已删除的答案中。但是问题变得太长了,如果您有兴趣,请参阅编辑历史记录。
以下程序在 C++17 中有明确定义的行为吗?

int main()
{
int a=5;
(a += 1) += a;
return a;
}
我相信这个表达式是明确定义和评估的:
  • 右侧a被评估为 5。
  • 右侧没有副作用。
  • 左侧被评估为对 a 的引用, a += 1是明确定义的。
  • 左侧的副作用被执行,使得 a==6 .
  • 对赋值进行评估,将 a 的当前值加 5 ,使其变为 11。

  • 标准的相关部分:
    [intro.execution]/8 :

    An expression X is said to be sequenced before an expression Y ifevery value computation and every side effect associated with theexpression X is sequenced before every value computation and everyside effect associated with the expression Y.


    [expr.ass]/1 (强调我的):

    The assignment operator (=) and the compound assignment operators allgroup right-to-left. All require a modifiable lvalue as their leftoperand; their result is an lvalue referring to the left operand. Theresult in all cases is a bit-field if the left operand is a bit-field.In all cases, the assignment is sequenced after the value computationof the right and left operands, and before the value computation ofthe assignment expression. The right operand is sequenced before theleft operand. With respect to an indeterminately-sequenced function call, the operation of a compound assignment is a single evaluation.


    措辞最初来自接受的论文 P0145R3 .
    现在,我觉得这第二部分有些含糊不清,甚至是矛盾的。

    The right operand is sequenced before the left operand.


    加上sequenced before的定义,强烈暗示了副作用的排序,而前一句:

    In all cases, the assignment is sequenced after the value computationof the right and left operands, and before the value computation ofthe assignment expression


    仅在值计算后显式排序赋值,而不是它们的副作用。因此允许这种行为:
  • 右侧a被评估为 5。
  • 左侧被评估为引用 a , a += 1是明确定义的。
  • 对赋值进行评估,将 a 的当前值加 5 ,使其变为 10。
  • 左侧的副作用被执行,使得 a==11或者甚至 6如果旧值甚至用于副作用。

  • 但是这种排序显然违反了sequenced before的定义,因为左操作数的副作用发生在右操作数的值计算之后。因此,左操作数没有在右操作数之后排序,这使上述句子紫罗兰色。 不,我做了傻事。这是允许的行为,对吗? IE。分配可以交错左右评估。或者可以在两次全面评估后完成。
    Running the code gcc 输出 12,clang 11。此外,gcc 警告
    <source>: In function 'int main()':

    <source>:4:8: warning: operation on 'a' may be undefined [-Wsequence-point]
    4 | (a += 1) += a;
    | ~~~^~~~~
    我在阅读汇编方面很糟糕,也许有人至少可以重写 gcc 如何达到 12? (a += 1), a+=a有效,但这似乎是错误的。
    好吧,仔细想想,右侧也确实评估为对 a 的引用。 ,而不仅仅是值 5。所以 Gcc 仍然可能是正确的,在这种情况下,clang 可能是错误的。

    最佳答案

    为了更好地遵循实际执行的内容,让我们尝试用我们自己的类型模仿相同的内容并添加一些打印输出:

    class Number {
    int num = 0;
    public:
    Number(int n): num(n) {}
    Number operator+=(int i) {
    std::cout << "+=(int) for *this = " << num
    << " and int = " << i << std::endl;
    num += i;
    return *this;
    }
    Number& operator+=(Number n) {
    std::cout << "+=(Number) for *this = " << num
    << " and Number = " << n << std::endl;
    num += n.num;
    return *this;
    }
    operator int() const {
    return num;
    }
    };
    然后当我们运行时:
    Number a {5};
    (a += 1) += a;
    std::cout << "result: " << a << std::endl;
    We get different results with gcc and clang (并且没有任何警告!)。
    海湾合作委员会:
    +=(int) for *this = 5 and int = 1
    +=(Number) for *this = 6 and Number = 6
    result: 12
    clang :
    +=(int) for *this = 5 and int = 1
    +=(Number) for *this = 6 and Number = 5
    result: 11
    这与问题中的整数结果相同。 即使它不是完全相同的故事:内置赋值有自己的排序规则,而不是作为函数调用的重载运算符,但相似性仍然很有趣。
    似乎 gcc 将右侧作为引用并在调用 += 时将其转换为值,而另一方面,clang 将右侧首先转换为值。
    下一步是向我们的 Number 类添加一个复制构造函数,以准确跟踪引用何时变为值。 Doing that结果是调用复制构造函数作为第一个操作,clang 和 gcc, 两者的结果相同:11 .
    似乎 gcc 延迟了对值转换的引用(在内置赋值以及没有用户定义复制构造函数的用户定义类型中)。 它与 C++17 定义的排序一致吗?对我来说,这似乎是一个 gcc 错误,至少对于问题中的内置赋值而言,因为听起来从引用到值的转换是“值计算”的一部分 that shall be sequenced before the assignment .

    至于 a strange behavior of clang在原始帖子的先前版本中报告 - 在断言和打印时返回不同的结果:
    constexpr int foo() {
    int res = 0;
    (res = 5) |= (res *= 2);
    return res;
    }

    int main() {
    std::cout << foo() << std::endl; // prints 5
    assert(foo() == 5); // fails in clang 11.0 - constexpr foo() is 10
    // fixed in clang 11.x - correct value is 5
    }
    这与 a bug in clang 有关.断言的失败是错误的,这是由于在编译时不断评估期间,此表达式在 clang 中的评估顺序错误。该值应为 5。此错误 is already fixed在叮当的行李箱中。

    关于c++ - 使用 C++17 排序的链式复合赋值仍然是未定义的行为?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64195482/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com