那么为什么i = ++ i + 1在C ++ 11中定义明确?

时间:2012-12-22 18:42:54

标签: c++ c++11 undefined-behavior language-lawyer

我看到了other类似的questions并阅读了defect。但我仍然没有得到它。当i = ++i + 1不是时,为什么{C} 11中i = i++ + 1定义良好?该标准如何明确定义?

通过我的研究,我在图之前有以下序列(其中箭头表示关系之前的顺序,除非另有说明,否则一切都是值计算):

i = ++i + 1
     ^
     |
assignment (side effect on i)
 ^      ^
 |      |
☆i   ++i + 1
     ||    ^
    i+=1   |
     ^     1
     |
★assignment (side effect on i)
  ^      ^
  |      |
  i      1

我在i标记了一个带有黑色星号的副作用,并使用白星计算了i的值。这些似乎相互之间没有相应的顺序(根据我的逻辑)。标准说:

  

如果对标量对象的副作用相对于同一标量对象的另一个副作用或使用相同标量对象的值进行的值计算未被排序,则行为未定义。

defect report中的解释并没有帮助我理解。左值到右值的转换与任何事情有什么关系?我弄错了什么?

3 个答案:

答案 0 :(得分:21)

  

...或使用相同标量对象的值 进行值计算 ...

这里重要的部分是粗体。左侧不使用i的值进行值计算。正在计算的是 glvalue 。仅在之后(之后排序),触摸并替换对象的值。

不幸的是,这是一个非常微妙的观点:)

答案 1 :(得分:16)

嗯,关键时刻,++i的结果是左值。并且为了参与二进制+,必须通过左值到右值转换将左值转换为右值。 Lvalue-to-rvalue转换基本上是从内存中读取变量i的行为。

这意味着++i的值应该是获得的,就像直接从i读取一样。这意味着概念上,i的新(递增)值必须准备好(必须物理存储在i)才能开始评估二进制+

这种额外的排序是无意中使得在这种情况下定义的行为。

在C ++ 03中,没有严格要求直接从++i获取i的值。可以将新值预测/预先分类为i + 1,并将其用作二进制+的操作数,甚至在它实际存储在实际i之前。虽然可以合理地声称这个要求是隐含的,即使在C ++ 03中(并且C ++ 03不承认它的存在是C ++ 03的缺陷)


如果是i++,则结果为 rvalue 。因为它已经是一个右值,所以没有涉及左值到右值的转换,在我们开始评估二进制i之前,绝对没有要求将它存储在+中。

答案 2 :(得分:4)

自从提出这个问题以来,我已经写了一篇关于visualising C++ evaluation sequencing with graphs的文章。这个问题与i = ++i的情况相同,后面的图表有以下顺序:

Sequenced-before graph for i = ++i

红色节点代表i的副作用。蓝色节点表示使用i的值的评估。这个表达式定义明确的原因是因为没有这些节点彼此之间没有相互排序。在分配左侧使用i并不使用i的值,因此不是问题。

我遗漏的主要部分是“使用价值”的意思。如果运算符期望该操作数的prvalue表达式,则运算符使用其操作数的值。给出一个对象的名称,这是一个左值,左值必须进行左值到右值的转换,这可以看作是“读取对象的值”。赋值只需要左值操作数的左值,这意味着它不使用它的值。