4

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

4

2 回答 2

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 循环或类似的东西中,而不是所有其他值) .
另请注意,从文档operator[]astd::valarray返回 a T&。这证实了上面的假设:它被1作为迭代的第一步,然后所有其他操作都无用。
只需复制它即可解决问题,如示例代码所示。

于 2016-02-25T13:26:55.260 回答
1

为什么会发生这种情况的细节是由于用于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]

于 2018-04-05T23:06:15.217 回答