C++17 排序:赋值左侧的后增量

Posted

技术标签:

【中文标题】C++17 排序:赋值左侧的后增量【英文标题】:C++17 sequencing: post-increment on left side of assignment 【发布时间】:2019-03-16 16:20:14 【问题描述】:

C++17标准通过stating, to the effect规则修改了C++语言的运算顺序定义:

在每个简单的赋值表达式 E1=E2 和每个复合 赋值表达式 E1@=E2,每个值的计算和副作用 E2 在每个值计算和副作用之前排序 E1

但是,在 GCC 8.1 中使用 -std=c++17-Wall 编译以下代码时

int v[]  0,1,2,3,4,5,6,7 ;
int *p0 = &v[0];
*p0++ = *p0 + 1;
cout << "v[0]: " << v[0] << endl;

我收到以下警告:

main.cpp:266:8: warning: operation on 'p0' may be undefined [-Wsequence-point]
     *p0++ = *p0 + 1;
      ~~^~

输出是:

v[0]: 1

问题是:警告是否错误?

【问题讨论】:

当然,标准表明*p0 + 1 将在左侧的任何内容之前进行评估。由于++ 是后缀,因此无论是否对其进行评估都不会影响对v[0] 的分配。真正的问题是p0 现在是否指向v[1] 无警告here @Jans 你要加-Wall @Jans 您已关闭警告。添加-Wall 【参考方案1】:

问题是:警告是否错误?

视情况而定。

从技术上讲,有问题的代码定义明确。在 C++17 中,右侧排序在左侧之前,而在它之前是不确定排序的。并且 gcc 正确编译代码,v[0] == 1 在分配之后。

但是,这也是不应该编写的糟糕代码,因此虽然警告的具体措辞是错误的,但警告的实际精神对我来说似乎很好。至少,我不打算提交有关它的错误报告,而且它似乎不值得开发人员花时间修复。 YMMV。

【讨论】:

当然,“糟糕的代码”往往在旁观者的眼中。有些人认为A &amp;&amp; B || C &amp;&amp; D 是糟糕的代码,因为他们从未研究过形式逻辑,也没有“长大”隐含地知道连词总是比析取具有更高的优先级。其他人认为(A &amp;&amp; B) || (C &amp;&amp; D) 是糟糕的代码,因为括号的使用暗示了非标准的评估顺序。关键是这种表达感觉就像是糟糕的代码,因为它是几十年来 C++ 编码中根深蒂固的思想习惯,那就是 UB。但现在不是。所以曾经可怕的是现在优雅的语法。【参考方案2】:

[我在下面留下我的答案以供参考,但further discussion 表明我在下面的答案不完整,其结论最终不正确。]

C++17 标准(草案here),[expr.ass],确实是这样写的:

[赋值运算符的]右操作数排在左操作数之前。

这对我来说和对你一样都是错误的。 @Barry 不喜欢您的示例代码,因此,为了避免分散问题的注意力,我测试了替代代码:

#include <iostream>

namespace 
    int a 3;

    int& left()
    
        std::cout << "in left () ...\n";
        return ++a;
    

    int right()
    
        std::cout << "in right() ...\n";
        return a *= 2;
    


int main()

    left() = right();
    std::cout << a << "\n";
    return 0;

输出(使用 GCC 6.3):

in left () ...
in right() ...
8

无论您是考虑打印的消息还是考虑计算的值 8,看起来好像 left 操作数在 right 操作数之前排序 - 这是有道理的,就目前而言作为高效的机器码

通常应该更愿意决定将计算结果存储在哪里 在实际计算结果之前。

我不同意@Barry。您可能已经发现了该标准的一个重要问题。有时间就举报吧。

更新

@SombreroChicken 补充道:

那只是因为 GCC 6.3 还没有正确实现 C++17。从 7.1 起,它首先评估正确,如 here.

输出:

in right() ...
in left () ...
6

【讨论】:

那只是因为 GCC 6.3 还没有正确实现 C++17。从 7.1 开始,它首先评估正确,如下所示:wandbox.org/permlink/6OtnRcfOaxivYfN2 @thb “您可能已经发现标准的一个重要问题,”我认为您的意思是编译器? @SombreroChicken 您似乎在密切关注 [C++],所以我怀疑您可能已经看过它,但如果您还没有看过:您的启发性评论和测试运行现在引发了一个相关的问题,并且答案:In the expression left() = right(), why is right() sequenced first? @thb 是的,我看到了。那里有很好的问题和答案。

以上是关于C++17 排序:赋值左侧的后增量的主要内容,如果未能解决你的问题,请参考以下文章

17排序算法-希尔排序

重新排序/重置自动增量主键

重新排序/重置自动增量主键

Java实现希尔排序(增量递减排序)

重新排序/重置自动增量主键

重新排序/重置自动增量主键