12

考虑以下 MCVE,其中我有两个值数组,其中w是两次v在此处尝试):

#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>&)'

问题的根源似乎是 decuced 类型v * 2(我假设因为明确写下类型有效,所以似乎正在发生一些隐式转换)。

查看参考说明,似乎operator*返回的内容可能与std::valarray<T>. 我不明白这样做的原因,但更令人费解的是,这似乎也适用于operator*=,除了我的auto作业在这里有效。我希望 和 的返回值operator*=operator*这里是相同的(增量参考)。

所以我的问题是:

  • 这是一个实施问题/错误吗?还是我错过了什么?
  • 参考说明背后的基本原理是什么(例如,为什么操作员可以返回可能不适用于std::begin/的不同内容std::end)?

(注意:我标记了这个问题 c++11,但它似乎也适用于 17 及以下的所有版本)

4

1 回答 1

13

有一种称为表达式模板的技巧,它可以提高复合表达式的效率,但使用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*2or b*2,而是将整个表达式向下传递,并r使用逐元素操作进行构造。

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

auto不会转换为valarray; 它只存储表达式模板对象。所以你的代码坏了。

我不知道哪些版本的标准允许这样做。无论如何,一些 valarray 实现使用它,它增加了很多效率。没有它, valarray 坦率地说很糟糕。

于 2019-05-20T14:14:47.507 回答