So why is i = ++i + 1 well-defined in C++11?

杀马特。学长 韩版系。学妹 提交于 2019-12-27 22:06:26

问题


I've seen the other similar questions and read the defect about it. But I still don't get it. Why is i = ++i + 1 well-defined in C++11 when i = i++ + 1 is not? How does the standard make this well defined?

By my working out, I have the following sequenced before graph (where an arrow represents the sequenced before relationship and everything is a value computation unless otherwise specified):

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

I've marked a side effect on i with a black star and value computation of i with a white star. These appear to be unsequenced with respect to each other (according to my logic). And the standard says:

If a side effect on a scalar object is unsequenced relative to either another side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

The explanation in the defect report didn't help me understand. What does the lvalue-to-rvalue conversion have to do with anything? What have I gotten wrong?


回答1:


... or a value computation using the value of the same scalar object ...

The important part is bolded here. The left hand side does not use the value of i for the value computation. What is being computed is a glvalue. Only afterwards (sequenced after), the value of the object is touched and replaced.

Unfortunately this is a very subtle point :)




回答2:


Well, the key moment here is that the result of ++i is an lvalue. And in order to participate in binary + that lvalue has to be converted to an rvalue by lvalue-to-rvalue conversion. Lvalue-to-rvalue conversion is basically an act of reading the variable i from memory.

That means that the value of ++i is supposed to be obtained as if it was read directly from i. That means that conceptually the new (incremented) value of i must be ready (must be physically stored in i) before the evaluation of binary + begins.

This extra sequencing is what inadvertently made the behavior defined in this case.

In C++03 there was no strict requirement to obtain the value of ++i directly from i. The new value could have been predicted/precalulated as i + 1 and used as an operand of binary + even before it was physically stored in the the actual i. Although it can be reasonably claimed that the requirement was implicitly there even in C++03 (and the fact that C++03 did not recognize its existence was a defect of C++03)


In case of i++, the result is an rvalue. Since it is already an rvalue, there's no lvalue-to-rvalue conversion involved and there's absolutely no requirement to store it in i before we start evaluating the binary +.




回答3:


Long since asking this question, I have written an article on visualising C++ evaluation sequencing with graphs. This question is the identical to the case of i = ++i, which has the following sequenced-before graph:

The red nodes represent side effects on i. The blue node represents an evaluation that uses the value of i. The reason this expression is well-defined is because there are none of these nodes unsequenced with each other. The use of i on the left of the assigment doesn't use the value of i, so is not a problem.

The main part that I was missing was what "uses the value" means. An operator uses the value of its operand if it expects a prvalue expression for that operand. When giving the name of an object, which is an lvalue, the lvalue has to undergo lvalue-to-rvalue conversion, which can be seen as "reading the value of the object". Assignment only requires an lvalue for its left operand, which means that it doesn't use its value.



来源:https://stackoverflow.com/questions/14005508/so-why-is-i-i-1-well-defined-in-c11

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!