为什么会发生这种情况的细节是由于用于valarray
提高性能的实现技巧。libstdc++ 和 libc++ 都对运算结果使用表达式模板valarray
,而不是立即执行运算。这是 C++ 标准中的 [valarray.syn] p3 明确允许的:
任何返回 a 的函数valarray<T>
都可以返回另一种类型的对象,只要它的所有 const 成员函数valarray<T>
也适用于这种类型。
在您的示例中发生的情况是,arr/arr[0]
它不会立即执行除法,而是返回一个对象,该对象_Expr<__divide, _Valarray, _Constant, valarray<double>, double>
具有对 的引用arr
和对 的引用arr[0]
。当将该对象分配给另一个对象时,将valarray
执行除法运算并将结果直接存储到分配的左侧(这避免了创建临时valarray
存储结果然后将其复制到左侧)。
因为在您的示例中,左侧是同一个对象,arr
这意味着一旦使用结果更新arr[0]
了第一个元素,表达式模板中存储的引用将引用不同的值。arr
换句话说,最终结果是这样的:
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;
for 循环的第一次迭代将设置arr[0]
为arr[0] / arr[0]
ie arr[0] = 1
,然后所有后续迭代都将设置arr[i] = arr[i] / 1
,这意味着值不会改变。
我正在考虑对 libstdc++ 实现进行更改,以便表达式模板将double
直接存储 a 而不是保存引用。这意味着arr[i] / divexpr.rhs
将始终评估arr[i] / 5
而不使用 的更新值arr[i]
。