std :: valarray的运算符*有什么问题?

时间:2019-05-20 14:02:33

标签: c++ c++11 operators language-lawyer valarray

考虑以下MCVE,其中我有两个值数组,其中wvtry it out here)的两倍:

#include <valarray>

using namespace std;

int main() {
  valarray<int> v { 1, 2, 3 };

  for ([[maybe_unused]] auto x : v) {} // Ok

  auto w = v * 2;     // Leads to failure in loop below
  //valarray<int> w = v * 2; // Works
  //auto w = v*=2;      // Works
  //auto w = v; w *= 2; // Works

  for ([[maybe_unused]] auto x : w) {} // Failure here
}

此示例无法在最后一个循环中使用clang和gcc编译(此处为gcc输出):

error: no matching function for call to 'begin(std::_Expr<std::__detail::_BinClos<std::__multiplies, std::_ValArray, std::_Constant, int, int>, int>&)'

问题的根源似乎是v * 2的递减类型(我认为因为显式地记录了该类型是可行的,所以似乎正在进行一些隐式转换)。

看着reference notes,看来operator*可能会返回与std::valarray<T>不同的东西。 我不明白这样做的原因,但令人困惑的是the same seem to apply to operator*=,除了我的auto分配在这里起作用。我希望operator*=operator*的返回值在这里是相同的(减去引用)。

所以我的问题是:

  • 这是一个实现问题/错误吗?还是我想念什么?
  • reference notes背后的原理是什么(例如,为什么操作员会返回与std::begin / std::end不兼容的其他内容)?

(注意:我标记了c ++ 11这个问题,但似乎也适用于所有版本,最高为17)

1 个答案:

答案 0 :(得分:12)

有一个技巧叫表达式模板,它可以提高复合表达式的效率,但是使用auto却会令人发指。

更改此:

auto w = v * 2;

对此:

std::valarray<int> w = v * 2;

您的代码有效。


要了解为什么要使用表达式模板,请尝试以下操作:

std::valarray<int> a={1,2,3},b{4,5,6},c={2,4,8};
std::valarray<int> r = (a+b*2)*c;

此处,表达式模板避免创建临时的valarray a+b*2b*2,而是向下传递整个表达式,并使用逐元素操作构造r

(a+b*2)*c中没有创建三元素的valarray临时对象-只是描述表达式结构和参数的一系列对象。将表达式分配给实际的valarray后,将在逐个元素的基础上对表达式进行求值。

但是auto不会转换为valarray;它只存储表达式模板对象。这样您的代码就会中断。

我不知道该标准的哪个版本允许这样做;不管怎样,某些valarray实现使用此方法,并且它增加了很多效率。没有它,valarray会很烂。