3

以下示例中的if语句来自我正在尝试再次构建的旧项目。很抱歉,这不是一个可验证的样本,因为它不会重现错误,它自己编译得很好。

enum S { };

struct R {
    S type, state;
    double height;
};

int main ()
{
    int rows;
    S alive, rc;
    double h;
    R *r, *n;

    // BEGIN
    if ( (((r = n-1)   ->type & rc) && h > r->height) ||
         (((r = n+1)   ->type & rc) && h > r->height) ||
         (((r = n-rows)->type & rc) && h > r->height) ||
         (((r = n+rows)->type & rc) && h > r->height) )
    {
        n->state = alive;
    }
    // END
}

但是,在项目中,使用相同的编译(clang 3.3,除了包含路径之外没有其他选项),它给出了

warning: multiple unsequenced modifications to 'r' [-Wunsequenced]

(指向子表达式r = n - 1)。项目中的if语句(标记之间BEGIN-END)是相同的,只是变量的类型可能不同。这里定义的类型只是粗略的近似,但我认为它们与警告并不真正相关。我已经能够将表达式简化为

if( (r = n) || (r = n) ) //...

同时仍然在项目中重现警告。

我的理解是,在评估 operator 的第一个操作数之后有一个序列点&&||当没有重载时)。所以我无法解释警告。

如果对表达式的形式有任何想法,无论类型如何,那都会有所帮助。

编辑

使用 Vaughn Cato 的评论,我还发现

if( ((r = n-1) ->type) || ((r = n+1) ->type) )

产生警告,而

if( bool((r = n-1) ->type) || bool((r = n+1) ->type) )

才不是。S实际上是一个模仿枚举的类,并且有一个自定义的底层类。它重载了按位运算符&|以及!用于掩码和隐式转换运算符到底层类型,在特定情况下是size_t. 我不知道这有多大帮助,但我很抱歉没有从一开始就披露更完整的信息。

4

2 回答 2

2

见 N3797 1.9/15

除非另有说明,否则单个运算符的操作数和单个表达式的子表达式的求值是无序的。

5.14/2:(&&操作员)
5.15/2:(||操作员)

如果对第二个表达式求值,则与第一个表达式关联的每个值计算和副作用在与第二个表达式关联的每个值计算和副作用之前排序。

对“副作用”的引用意味着 的值r已正确排序。

我的阅读是这个警告是不正确的。该表达式已正确排序(不包括此处未显示的代码的任何奇怪效果)。

于 2014-04-02T14:00:09.030 回答
1

这里有一个常见的混淆。仅仅因为||&&是排序点并不意味着运行时r按照您认为的顺序进行评估。

它可以在任何表达式以它喜欢的任何顺序评估之前r = n - 1进行评估等;未排序。r = n + 1if

这就是编译器警告向您突出显示的内容。

顺便说一句,即使是声称具有更好定义顺序的语言(例如Java)也会受到我在这里提请您注意的效果的影响!

于 2014-04-02T13:11:35.330 回答