以下示例中的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) ) //...
同时仍在项目中复制警告。
我的理解是在评估运算符&&
,||
的第一个操作数之后有一个序列点(当没有超载时)。所以我无法解释警告。
如果有任何关于表达形式的想法,无论其类型如何,那都会有所帮助。
修改
使用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
。我不知道这有多大帮助,但我很抱歉从一开始就没有透露更完整的信息。
答案 0 :(得分:2)
见N3797 1.9 / 15
除非另有说明,否则对单个操作符的操作数和单个表达式的子表达式的评估是不合理的。
5.14 / 2 :( &&
运营商)
5.15 / 2 :( ||
运算符)
如果计算第二个表达式,则每个值计算和副作用都相关联 第一个表达式在每个值计算和与第二个表达式相关的副作用之前进行排序。
对副作用的引用'表示r
的值已正确排序。
我的解读是这个警告不正确。表达式已正确排序(不包括此处未显示的代码的任何奇怪效果)。
答案 1 :(得分:1)
这里有一个常见的混乱。仅仅因为||
和&&
是排序点,不意味着运行时按您认为的顺序评估r
。
它可以评估r = n - 1
,r = n + 1
等之前任何if
表达式都会在中计算出来的任何顺序 ; 即未被排除。
这就是编译器警告突出显示的内容。
顺便说一句,即使是声称具有更好定义排序的语言(例如Java)也会受到我引起你注意的影响!