Or is this particular question an exception to the other sequencing rules? |
it's not an exception from sequencing rules, but it appears to be complex enough to avoid being flagged by gcc or clang
Let's walk through C++14
the expression is
s[i]^=s[j]^=s[i]^=s[j]
.
First, apply "group right-to-left", and get
s[i] ^= (s[j] ^= (s[i] ^= s[j]))
now look at the leftmost ^=, whose left operand is
s[i]
and whose right operand is
(s[j] ^= (s[i] ^= s[j]))
Your quote says "the assignment is sequenced after the value computation of the right and left operands" - fine, but irrelevant. What you want to know is how are the left and right operands sequenced with respect to each other.
C++17 added to this paragraph "The right operand is sequenced before the left operand", but C++14 does not have that. in C++14 you'll have to go to [intro.execution]p15
"Except where noted, evaluations of operands of individual operators ... are unsequenced"
this means evaluations of
s[i]
and of
(s[j] ^= (s[i] ^= s[j]))
are unsequenced.
Now we got to undefined behavior (same paragraph): "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, and they are not potentially concurrent (1.10), the behavior is undefined.".
Here, value computation of s[i] (read from memory) in the left operand is unsequenced relative to both value computation (read from memory) and side-effect (write to memory) made to s[i] within
(s[j] ^= (s[i] ^= s[j]))
. Therefore, undefined behavior.