Valarray划分其元素

valarray divide its element

本文关键字:元素 划分 Valarray      更新时间:2023-10-16

当我将一个 valarray 除以它的第一个元素时,只有第一个元素变成 1,其他元素保持其原始值。

#include <iostream>
#include <valarray>
using namespace std;
int main() {
    valarray<double> arr({5,10,15,20,25});
    arr=arr/arr[0]; // or arr/=arr[0];
    for(int value:arr)cout << value << ' ';
    return 0;
}

实际输出为:

1 10 15 20 25

预期输出为:

1 2 3 4 5

为什么实际输出与预期不符?

我将 g++(4.8.1) 与 -std=c++11 一起使用

这个工作:

#include <iostream>
#include <valarray>
using namespace std;
int main() {
    valarray<double> arr({5,10,15,20,25});
    auto v = arr[0];
    arr=arr/v; // or arr/=arr[0];
    for(int value:arr)cout << value << ' ';
    return 0;
}

问题是您正在尝试使用同时修改的数组中的值(arr[0] arr )。
直观地说,一旦你通过执行arr[0]/arr[0]更新了arr[0],它包含什么价值?
好吧,这就是从现在开始将用于除以其他值的值......
请注意,这同样适用于arr/=arr[0](首先,arr[0]/arr[0]发生在 for 循环或类似的东西中,而不是所有其他)。
另请注意,从文档中operator[] std::valarray返回一个T&。这证实了上面的假设:它作为迭代的第一步转向1,然后所有其他操作都是无用的。
通过简单地复制它就可以解决问题,如示例代码所示。

发生这种情况的原因的详细信息是由于valarray中使用的实现技巧来提高性能。libstdc++ 和 libc++ 都使用表达式模板来获取valarray操作的结果,而不是立即执行操作。这是 [valarray.syn] p3 在C++标准中明确允许的:

任何返回 valarray<T> 的函数都可以返回其他类型的对象,前提是所有 valarray<T>的常量成员函数也适用于此类型。

在您的示例中发生的情况是,arr/arr[0]不会立即执行除法,而是返回一个对象,例如 _Expr<__divide, _Valarray, _Constant, valarray<double>, double> 该对象具有对arr的引用和对arr[0]的引用。将该对象分配给另一个valarray执行除法运算,并将结果直接存储到分配的左侧(这样可以避免创建临时valarray来存储结果,然后将其复制到左侧)。

因为在示例中,左侧是同一对象,arr ,这意味着一旦使用结果更新arr中的第一个元素,对存储在表达式模板中的arr[0]的引用将引用不同的值。

换句话说,最终结果是这样的:

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]arr[0] = 1,然后所有后续迭代都将设置arr[i] = arr[i] / 1这意味着值不会更改。

我正在考虑对 libstdc++ 实现进行更改,以便表达式模板将直接存储double而不是保存引用。这意味着arr[i] / divexpr.rhs将始终评估arr[i] / 5,而不是使用更新的值 arr[i]