valarray 除元素

valarray divide its element

当我将 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) 中的值 (arr[0])。
直观地说,一旦您通过 arr[0]/arr[0] 更新了 arr[0],它包含什么值?
好吧,这就是从现在开始除以其他值的值...
请注意,这同样适用于 arr/=arr[0](首先,arr[0]/arr[0] 比其他所有发生在 for 循环或类似的循环中)。
还要注意从documentation一个std::valarray一个returns一个T&那个operator[]。这证实了上面的假设:它转向1作为你迭代的第一步,那么其他所有操作都没有用。
只需复制它即可解决问题,如示例代码所示。

发生这种情况的详细信息是由于 valarray 中为提高性能而使用的实施技巧。 libstdc++ 和 libc++ 都使用 expression templates 作为 valarray 操作的结果,而不是立即执行操作。这是 C++ 标准中的 [valarray.syn] p3 明确允许的:

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.

在您的示例中发生的情况是 arr/arr[0] 没有立即执行除法,而是 returns 一个像 _Expr<__divide, _Valarray, _Constant, valarray<double>, double> 这样的对象引用了 arr 和对 arr[0] 的引用。当该对象被分配给另一个 valarray 时,执行除法运算并将结果直接存储到分配的 left-hand 端(这避免了创建一个临时的 valarray 来存储结果然后将其复制到 left-hand 侧)。

因为在您的示例中,left-hand 端是同一个对象,arr,这意味着一旦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-loop 的第一次迭代会将 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].

的更新值