valarray划分它的元素

时间:2016-02-25 13:20:42

标签: c++ c++11 valarray

当我将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

2 个答案:

答案 0 :(得分:3)

这个有效:

#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循环中或类似的东西)。
另请注意documentation operator[] std::valarray的{​​{1}}会返回T&。这证实了上面的假设:它作为迭代的第一步转向1,然后所有其他操作都无用。
通过简单地复制它就可以解决问题,如示例代码中所示。

答案 1 :(得分:1)

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

  

任何返回valarray<T>的函数都被允许返回另一种类型的对象,只要提供了所有   valarray<T>的const成员函数也适用于此类型。

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

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

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

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,这意味着值为“{1}} ;改变。

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