c++incrementundefined-behaviorfunction-callstd-tie

Is an Incremented Variable Reusable in a tie Call?


So I understand that re-usage of a variable that has been post incremented is undefined behavior in a function call. My understanding is this is not a problem in constructors. My question is about tie which is oddly halfway between each.

Given: pair<int, int> func() can I do:

tie(*it++, *it) = func();

Or is that undefined behavior?


Solution

  • Since C++17, this code has unspecified behavior. There are two possible outcomes:

    Per [expr.call]/8:

    [...] The initialization of a parameter, including every associated value computation and side effect, is indeterminately sequenced with respect to that of any other parameter. [...]

    So the second argument to tie may be either the result of dereferencing the incremented iterator or the original iterator.


    Prior to C++17, the situation was a bit complicated:

    Per N4140 (C++14 draft) [expr.call]/8:

    [ Note: The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered (see [intro.execution]). — end note ]

    Thus, the code was undefined behavior because the evaluation of one argument was unsequenced with the other. The evaluation of the two arguments may overlap, resulting in a data race. Unless it is specified otherwise ...

    Per N4140 [intro.execution]/15:

    When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [ Note: Value computations and side effects associated with different argument expressions are unsequenced. — end note ] Every evaluation in the calling function (including other function calls) that is not otherwise specifically sequenced before or after the execution of the body of the called function is indeterminately sequenced with respect to the execution of the called function.9 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 [expr.new]. For another example, invocation of a conversion function ([class.conv.fct]) can arise in contexts in which no function call syntax appears. — end example ] The sequencing constraints on the execution of the called function (as described above) are features of the function calls as evaluated, whatever the syntax of the expression that calls the function might be.

    9) In other words, function executions do not interleave with each other.

    Thus, if the operators are actually function calls, then the behavior is similarly unspecified.