c++ - 具有算术运算返回类型的 valarray

标签 c++ gcc g++ valarray

当我用 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";
}

reference表示实现可以选择算术运算重载的返回类型可能不是 valarray -value 但“表现得像它”的东西:

The operators returning a valarray by value are allowed to return an object of a different type instead. Such a type is required to be implicitly convertible to valarray and be supported as argument for all functions taking valarray& arguments. This allows copy-on-write implementations.

好吧,我的 operator<<应该要求那种“隐式转换”,不是吗?

那么为什么会出现段错误?

$ ./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)

当我尝试 clang(在 linux 上,所以可能是 gcc 的 stdlib)时,我对此持怀疑态度......它有效:

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 ]

好吧,在我提交 gcc-bug 之前……我做错了什么吗?是我的auto邪恶的?还是真的是 gcc?

最佳答案

这是因为 GCC 的 valarray实现使用 Expression Templates避免为算术表达式的中间结果创建临时对象。表达式模板和 auto不要混合好。

发生的是 ( a + b )不立即执行乘法,而是创建一个“闭包”对象,该对象具有对 a 的引用和 b .实际乘法将被延迟,直到在需要结果的上下文中使用闭包。接下来,表达式的其余部分 ( a + b ) / 2创建第二个闭包对象,该对象包含对第一个闭包对象的引用,以及对值 2 的引用.然后使用第二个闭包对象来初始化一个由 auto 推导出的类型的变量。 :

auto y = ( a + b ) / 2;

所以 y是一个闭包对象,它引用了第一个闭包和 int有值 2 .但是,第一次关闭和int value 都是临时变量,在语句末尾超出范围。这意味着 y有两个悬空引用,一个临时关闭和一个临时 int .当您尝试使用 ycout声明它被转换为 valarray<double>它试图评估乘法和除法的结果。该评估遵循悬空引用并尝试访问不再存在的临时对象。这意味着未定义的行为。

我正在为 GCC 开发一个补丁,这将有助于使这样的代码更不容易出错(对于 Bug 83860 ),尽管结合 auto 仍然很脆弱带有表达式模板。

如果您不使用 auto,代码可以正常工作即

std::valarray<double> y = (a+b)/2;

这里的表达式模板在临时对象超出范围之前被评估,因此没有悬空引用。

这个特殊的例子可以通过编译 -fstack-reuse=none 来“工作”。这禁用了重用临时对象使用的堆栈空间的优化。这意味着悬空引用在其生命周期结束后仍可用于访问临时对象。这只是一个创可贴,而不是真正的解决方案。真正的解决方案是不要将表达式模板和 auto 混合使用。 .

关于c++ - 具有算术运算返回类型的 valarray,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42726804/

相关文章:

c++ - 谷歌模拟 ByRef 方法

c - 升级到 Windows 10 后不再具有通过 gcc 编译的权限

c++ - g+++strncat : might overflow destination buffer

c++ - 使用 Xerces-C 在我的 xml 中添加样式表声明

c++ - 使用 Xcode 9.0 with clang 时出现编译错误(生成多个输出文件时不能指定 -o)

c - Windows Defender/安全性删除我的 “hello world”程序

c++ - const 条件的分支预测

c++ - 通过 C++ 编译器构建 C 应用程序

c++ - C++中的引用调用

c - gcc 检查文件是否是主文件 (#if __BASE_FILE__ == __FILE__)