简体   繁体   English

具有算术运算的valarray返回类型

[英]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 )不立即执行乘法,而是创建一个“闭包”对象,它引用了ab 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是一个闭包对象,它引用了第一个闭包,并引用了一个值为2int 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM