std::valarray 的 operator* 有什么问题?
What's wrong with std::valarray's operator*?
考虑以下 MCVE,其中我有两个值数组,其中 w
是 v
的两倍(try 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
的 decuced 类型(我假设因为显式写下类型有效,所以似乎发生了一些隐式转换)。
查看 reference notes,似乎 operator*
可能 return 与 std::valarray<T>
有所不同。
我不明白这样做的原因,但更令人费解的是 the same seem to apply to operator*=
,除了这里我的 auto
作业有效。我希望 operator*=
和 operator*
的 return 值在这里相同(增量参考)。
所以我的问题是:
- 这是一个实现吗issue/bug?还是我遗漏了什么?
- reference notes 背后的基本原理是什么(例如,为什么运算符 return 可能不适用于
std::begin
/std::end
的不同内容)?
(注意:我将这个问题标记为 c++11,但它似乎也适用于 17 及以下的所有版本)
有一个称为表达式模板的技巧可以提高复合表达式的效率,但会严重破坏 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*2
或 b*2
,而是将整个表达式向下传递,并使用 element-wise 操作构造 r
。
(a+b*2)*c
中没有创建 3 元素 valarray 临时对象——只是一系列描述表达式结构和参数的对象。当分配给实际的 valarray
时,表达式将在 element-by-element 基础上计算。
但是 auto
没有转换为 valarray
;它只存储表达式模板对象。所以你的代码中断了。
我不知道哪个版本的标准允许这样做;不管怎样,一些 valarray 实现使用它,它增加了很多效率。没有它,valarray 坦率地说很烂。
考虑以下 MCVE,其中我有两个值数组,其中 w
是 v
的两倍(try 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
的 decuced 类型(我假设因为显式写下类型有效,所以似乎发生了一些隐式转换)。
查看 reference notes,似乎 operator*
可能 return 与 std::valarray<T>
有所不同。
我不明白这样做的原因,但更令人费解的是 the same seem to apply to operator*=
,除了这里我的 auto
作业有效。我希望 operator*=
和 operator*
的 return 值在这里相同(增量参考)。
所以我的问题是:
- 这是一个实现吗issue/bug?还是我遗漏了什么?
- reference notes 背后的基本原理是什么(例如,为什么运算符 return 可能不适用于
std::begin
/std::end
的不同内容)?
(注意:我将这个问题标记为 c++11,但它似乎也适用于 17 及以下的所有版本)
有一个称为表达式模板的技巧可以提高复合表达式的效率,但会严重破坏 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*2
或 b*2
,而是将整个表达式向下传递,并使用 element-wise 操作构造 r
。
(a+b*2)*c
中没有创建 3 元素 valarray 临时对象——只是一系列描述表达式结构和参数的对象。当分配给实际的 valarray
时,表达式将在 element-by-element 基础上计算。
但是 auto
没有转换为 valarray
;它只存储表达式模板对象。所以你的代码中断了。
我不知道哪个版本的标准允许这样做;不管怎样,一些 valarray 实现使用它,它增加了很多效率。没有它,valarray 坦率地说很烂。