Undefined behavior and sequence points reloaded

时光怂恿深爱的人放手 提交于 2019-11-25 20:23:52

It looks like the code

i.operator+=(i.operator ++());

Works perfectly fine with regards to sequence points. Section 1.9.17 of the C++ ISO standard says this about sequence points and function evaluation:

When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function.

This would indicate, for example, that the i.operator ++() as the parameter to operator += has a sequence point after its evaluation. In short, because overloaded operators are functions, the normal sequencing rules apply.

Great question, by the way! I really like how you're forcing me to understand all the nuances of a language that I already thought I knew (and thought that I thought that I knew). :-)

http://www.eelis.net/C++/analogliterals.xhtml Analog literals comes to my mind

  unsigned int c = ( o-----o
                     |     !
                     !     !
                     !     !
                     o-----o ).area;

  assert( c == (I-----I) * (I-------I) );

  assert( ( o-----o
            |     !
            !     !
            !     !
            !     !
            o-----o ).area == ( o---------o
                                |         !
                                !         !
                                o---------o ).area );

As others have said, your i += ++i example works with the user-defined type since you're calling functions, and functions comprise sequence points.

On the other hand, a[++i] = i is not so lucky assuming that a is your basic array type, or even a user defined one. The problem you've got here is that we don't know which part of the expression containing i is evaluated first. It could be that ++i is evaluated, passed off to operator[] (or the raw version) in order to retrieve the object there, and then the value of i gets passed to that (which is after i was incremented). On the other hand, perhaps the latter side is evaluated first, stored for later assignment, and then the ++i part is evaluated.

I think it's well-defined:

From the C++ draft standard (n1905) §1.9/16:

"There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function13) . Several contexts in C++ cause evaluation of a function call, even though no corresponding function call syntax appears in the translation unit. [ Example: evaluation of a new expression invokes one or more allocation and constructor functions; see 5.3.4. For another example, invocation of a conversion function (12.3.2) can arise in contexts in which no function call syntax appears. — end example ] The sequence points at function-entry and function-exit (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be. "

Note the part I bolded. This means there is indeed a sequence point after the increment function call (i.operator ++()) but before the compound assignment call (i.operator+=).

Nawaz

Alright. After going through previous responses, I re-thought about my own question, particularly this part that only Noah attempted to answer but I'm not convinced with him completely.

a[++i] = i;

Case 1:

If a is an array of built-in type. Then what Noah said is correct. That is,

a[++i] = i is not so lucky assuming that a is your basic array type, or even a user defined one . The problem you've got here is that we don't know which part of the expression containing i is evaluated first.

So a[++i]=i invokes undefined-behavior, or the result is unspecified. Whatever it is, it's not well-defined!

PS: In above quotation, strike-through is of course mine.

Case 2:

If a is an object of user-defined type which overloads the operator[], then again there are two cases.

  1. If the return type of overloaded operator[] function is built-in type, then again a[++i]=i invokes undefined-behavior or the result is unspecified.
  2. But if the return type of overloaded operator[] function is user-defined type, then the behavior of a[++i] = i is well-defined (as far as I understand), since in this case a[++i]=i is equivalent to writing a.operator[](++i).operator=(i); which is same as, a[++i].operator=(i);. That is, assignment operator= gets invoked on the returned object of a[++i], which seems be very well-defined, since by the time a[++i] returns, ++i have already been evaluated, and then the returned object calls operator= function passing the updated value of i to it as argument. Note that there is a sequence point between these two calls. And the syntax ensures that there is no competition between these two calls, and operator[] would get invoked first, and consecutively, the argument ++i passed into it, would also get evaluated first.

Think of this as someInstance.Fun(++k).Gun(10).Sun(k).Tun(); in which each consecutive function call returns an object of some user-defined type. To me, this situation seems more like this: eat(++k);drink(10);sleep(k), because in both situations, there exists sequence point after each function call.

Please correct me if I'm wrong. :-)

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