The details of why this happens are due to implementation tricks used in valarray
to improve performance. Both libstdc++ and libc++ use expression templates for the results of valarray
operations, rather than performing the operations immediately. This is explicitly allowed by [valarray.syn] p3 in the C++ standard:
Any function returning a valarray<T>
is permitted to return an object of another type, provided all the
const member functions of valarray<T>
are also applicable to this type.
What happens in your example is that arr/arr[0]
doesn't perform the division immediately, but instead it returns an object like _Expr<__divide, _Valarray, _Constant, valarray<double>, double>
which has a reference to arr
and a reference to arr[0]
. When that object is assigned to another valarray
the division operation is performed and the result stored directly into the left-hand side of the assignment (this avoids creating a temporary valarray
to store the result and then copying it into the left-hand side).
Because in your example the left-hand side is the same object, arr
, it means that the reference to arr[0]
stored in the expression template refers to a different value once the first element in arr
has been updated with the result.
In other words, the end result is something like this:
valarray<double> arr{5, 10, 15, 20, 25};
struct DivisionExpr {
const std::valarray<double>& lhs;
const double& rhs;
};
DivisionExpr divexpr = { arr, arr[0] };
for (int i = 0; i < size(); ++i)
arr[i] = divexpr.lhs[i] / divexpr.rhs;
The first iteration of the for-loop will set arr[0]
to arr[0] / arr[0]
i.e. arr[0] = 1
, and then all subsequent iterations will set arr[i] = arr[i] / 1
which means the values don't change.
I'm considering making a change to the libstdc++ implementation so that the expression template will store a double
directly instead of holding a reference. This would mean arr[i] / divexpr.rhs
will always evaluate arr[i] / 5
and not use the updated value of arr[i]
.