[英]valarray with arithmetic operations return type
When I write a simple arithmetic expression with valarray
and assign the result to auto
I get a segfault when I try to access the result on gcc. 当我用
valarray
编写一个简单的算术表达式并将结果赋值给auto
时,当我尝试访问gcc上的结果时,我得到一个段错误。
#include <iostream>
#include <valarray>
using std::ostream; using std::valarray;
ostream& operator<<(ostream&os, const valarray<double>&vs) {
os << "[";
for(auto&v : vs) os << v << " ";
return os << "]";
}
int main() {
valarray<double> a{ 1.0, 2.0, 3.0, 4.0 };
std::cout << "a: " << a << "\n";
valarray<double> b{ 2.0, 4.0, 6.0, 8.0 };
std::cout << "b: " << b << "\n";
valarray<double> c{ 2.0, 1.5, 0.5, 0.25 };
std::cout << "c: " << c << "\n";
valarray<double> x = ( a + b ) / 2;
std::cout << "x: " << x << "\n";
// this still works:
auto y = ( a + b ) / 2;
// The following will result in a segfault:
std::cout << "y:" << y << "\n";
}
The reference says that the implementation may choose that the return type of the arithmetic operation overloads may not be a valarray
-value but something that "behaves like it": 该引用表示实现可能会选择算术运算重载的返回类型可能不是
valarray
而是“行为类似”:
The operators returning a valarray by value are allowed to return an object of a different type instead.
按值返回valarray的运算符允许返回不同类型的对象。 Such a type is required to be implicitly convertible to valarray and be supported as argument for all functions taking valarray& arguments.
这种类型需要隐式转换为valarray,并且作为参与valarray和参数的所有函数的参数。 This allows copy-on-write implementations.
这允许写时复制实现。
Well, my operator<<
should call for that "implicit conversion", shouldnt it? 好吧,我的
operator<<
应该要求那个“隐式转换”,不应该吗?
So why do I get a segfault? 那么为什么我会遇到段错误呢?
$ ./valarray01.cpp.x
a: [1 2 3 4 ]
b: [2 4 6 8 ]
c: [2 1.5 0.5 0.25 ]
x: [1.5 3 4.5 6 ]
Segmentation fault (core dumped)
gcc version 6.2.0 20160901 (Ubuntu 6.2.0-3ubuntu11~14.04)
gcc版本6.2.0 20160901(Ubuntu 6.2.0-3ubuntu11~14.04)
I got sceptical when I tried clang (on linux, so probably gcc's stdlib) and... it works: 当我尝试使用clang (在Linux上,所以可能是gcc的stdlib)时,我对此持怀疑态度......它的工作原理如下:
clang version 3.9.1-svn288847-1~exp1 (branches/release_39)
clang version 3.9.1-svn288847-1~exp1(branches / release_39)
$ ./valarray01.cpp.x
a: [1 2 3 4 ]
b: [2 4 6 8 ]
c: [2 1.5 0.5 0.25 ]
x: [1.5 3 4.5 6 ]
y:[1.5 3 4.5 6 ]
Well, before I file a gcc-bug... am I doing something wrong? 好吧,在我提交gcc-bug之前......我做错了什么? Is my
auto
evil? 我的
auto
邪恶? Or is it really gcc? 还是真的是gcc?
This happens because GCC's valarray
implementation uses Expression Templates to avoid temporary objects being created for intermediate results of arithmetic expressions. 这是因为GCC的
valarray
实现使用表达式模板来避免为算术表达式的中间结果创建临时对象。 Expression templates and auto
do not mix well. 表达模板和
auto
混合不好。
What happens is that ( a + b )
does not perform the multiplication immediately, instead it creates a "closure" object which has references to a
and b
. 会发生的是
( a + b )
不立即执行乘法,而是创建一个“闭包”对象,它引用了a
和b
。 The actual multiplication will be delayed until the closure is used in a context that requires the result. 实际的乘法将被延迟,直到在需要结果的上下文中使用闭包。 Next, the rest of the expression
( a + b ) / 2
creates a second closure object which holds a reference to the first closure object, and a reference to the value 2
. 接下来,表达式
( a + b ) / 2
的其余部分创建第二个闭包对象,该对象包含对第一个闭包对象的引用,以及对值2
的引用。 That second closure object is then used to initialize a variable with the type deduced by auto
: 然后使用第二个闭包对象来初始化一个由
auto
推导出的类型的变量:
auto y = ( a + b ) / 2;
So y
is a closure object that has a reference to the first closure, and to an int
with value 2
. 所以
y
是一个闭包对象,它引用了第一个闭包,并引用了一个值为2
的int
。 However, the first closure and the int
value were both temporaries, which go out of scope at the end of the statement. 但是,第一个闭包和
int
值都是临时值,它们在语句结束时超出了范围。 This means that y
has two dangling references, to a temporary closure and to a temporary int
. 这意味着
y
有两个悬空引用,一个临时闭包和一个临时int
。 When you try to use y
in the cout
statement it gets converted to a valarray<double>
which tries to evaluate the results of the multiplication and the division. 当你尝试在
cout
语句中使用y
,它会转换为valarray<double>
,它会尝试计算乘法和除法的结果。 That evaluation follows the dangling references and tries to access temporary objects that no longer exist. 该评估遵循悬空引用并尝试访问不再存在的临时对象。 That means undefined behaviour.
这意味着未定义的行为。
I'm working on a patch for GCC that will help make code like this less error-prone (for Bug 83860 ), although it will still be fragile to combine auto
with expression templates. 我正在开发一个GCC补丁,它可以帮助使这样的代码不那么容易出错(对于Bug 83860 ),尽管将
auto
与表达模板结合起来仍然很脆弱。
The code works fine if you don't use auto
ie 如果你不使用
auto
ie,代码工作正常
std::valarray<double> y = (a+b)/2;
Here the expression templates get evaluated before the temporaries go out of scope, and so there are no dangling references. 这里表达模板在临时表超出范围之前进行评估,因此没有悬空引用。
This particular example can be made to "work" by compiling with -fstack-reuse=none
which disables the optimizations that reuse the stack space used by the temporary objects. 通过使用
-fstack-reuse=none
进行编译,可以使此特定示例“工作”,从而禁用重用临时对象使用的堆栈空间的优化。 This means the dangling references can still be used to access the temporaries after their lifetime ends. 这意味着悬挂引用仍可用于在终身结束后访问临时对象。 This is just a band-aid, not a real solution.
这只是一个创可贴,而不是一个真正的解决方案。 The real solution is to not mix expression templates and
auto
. 真正的解决方案是不混合表达模板和
auto
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.