c++functionlanguage-lawyerexpressionside-effects

Side effects and undefined behaviour when references are used in a function


We all know that the expression i++ + i++ is undefined behavior. But is it also UB, if the side effects happen on a reference in the function body? For example :

int f(int& i) {   // pass by reference
    return i++;  
}

int main() {
    int v=5;  
    auto r=f(v)+ 2*f(v); 
    cout << r <<endl;  
}

When I read the standard, I understand that this is UB:

Therefore, since both calls to f() modify the same memory location v, I understand it's UB. However some argue that the execution of the two called function bodies would be indeterminately sequenced, and not unsequenced, and hence the code would just deliver an unspecified result (17 if left call is executed first, and 16 if the contrary), without being UB. Can some language-lawyer enlighten me?


Solution

  • [intro.execution]/11 effectively states that every function invocation happens atomically with respect to sequencing, i.e. multiple function calls can't be interleaved.

    Specifically here i++ for the left-hand invocation of f is required to either be sequenced-before or sequenced-after, i.e. indeterminately-sequenced with, all evaluations in the right-hand side call to f, because both happen on the same thread and the former evaluation does not occur within the latter function invocation.

    As a result evaluations in different function calls (or different full-expressions in general) can't be unsequenced relative to one-another (if they happen on the same thread).

    Therefore this is not undefined behavior. The two i++ evaluations are indeterminately sequenced, so you do not know which happens first, but they are not unsequenced.

    There are however two valid observable behaviors, i.e. two different possible outputs, for either ordering of the function call evaluations.