简体   繁体   English

valarray就地操作给出不同的结果作为临时赋值

[英]valarray in-place operation gives different result as a temporary assignment

the following program: 以下程序:

#include<iostream>
#include<valarray>

using namespace std;

int main() {
  int init[] = {1, 1};

  // Example 1
  valarray<int> a(init, 2);
  // In-place assignment
  a[slice(0, 2, 1)] = a[slice(0, 2, 1)] + valarray<int>(a[slice(0, 2, 1)]) * a[0];

  for (int k = 0; k < 2; ++ k) {
    cout << a[k] << ' ';  // Outputs 2 3
  }
  cout << endl;

  // Example 2
  valarray<int> b(init, 2);
  // Temporary assignment
  valarray<int> r = b[slice(0, 2, 1)] + valarray<int>(b[slice(0, 2, 1)]) * b[0];
  b[slice(0, 2, 1)] = r;

  for (int k = 0; k < 2; ++ k) {
    cout << b[k] << ' '; // Outputs 2 2
  }
  cout << endl;
  return 0;
}

outputs: 输出:

2 3
2 2

The correct answer is 2 2 ( <1 1> + <1 1> * 1 = <2 2> . Why is the inline version outputting something different? 正确答案是2 2<1 1> + <1 1> * 1 = <2 2> 。为什么内联版本输出的内容有所不同?

In case it matters, I'm compiling this way: 以防万一,我以这种方式编译:

g++ myprogram.cpp -o myprogram

And the output of g++ -v is: g++ -v的输出是:

Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 5.4.0-6ubuntu1~16.04.5' --with-bugurl=file:///usr/share/doc/gcc-5/README.Bugs --enable-languages=c,ada,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-5 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-libmpx --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-5-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-5-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-5-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.5) 

This looks like a missing template overload with the older compiler. 看起来旧的编译器缺少模板重载。 Template 模板

valarray<T>& operator=( valarray<T>&& other ) noexcept;

exists in VS2017 and in gcc-7.1 but absent in older versions. 在VS2017和gcc-7.1中存在,但在较早版本中不存在。 The expression on the right seems to be evaluated and assigned every iteration. 似乎每次迭代都会评估和分配右侧的表达式。 The simplest example demonstrating this: 最简单的示例证明了这一点:

#include <iostream>
#include <valarray>

int main() 
{

    std::valarray<int> a{2, 4, 8};
    a = a + a[0];

    for (auto n : a) 
        std::cout << n << " ";

    std::cout << std::endl << std::endl;

    return 0;
}

The correct output is: 正确的输出是:

4 6 10

However older compilers produce 但是较旧的编译器会产生

4 8 12

Solution would be to use updated compiler or force copy 解决方案是使用更新的编译器或强制复制

a = std::valarray<int>(a + a[0]);

Hope this helps 希望这可以帮助

First, a[slice(0, 2, 1)] has type slice_array<T> , and there is no overload of operator+ taking an slice_array<T> object or reference as parameter. 首先, a[slice(0, 2, 1)] slice_array<T> a[slice(0, 2, 1)]类型为slice_array<T> ,并且没有operator+slice_array<T>对象或引用作为参数的重载。

Note the possible working overload operator+(const valarray<T>&, const valarray<T>&) is a function template, though slice_array<T> can be implicitly converted to valarray<T> , the template argument T cannot be deduced from the slice_array<T> argument. 请注意,可能的工作重载operator+(const valarray<T>&, const valarray<T>&)是一个函数模板,尽管slice_array<T>可以隐式转换为valarray<T> ,但模板参数T不能从slice_array<T>参数。

So strictly speaking, your code will cause a compile error. 因此严格来说,您的代码将导致编译错误。 In fact, Clang does . 实际上, Clang确实有


Second, you should know there are some optimization techniques for operations of valarray . 其次,您应该知道valarray操作有一些优化技术。 One well-konwn technique is expression templates , which causes your unexpected results. 表达式模板是一种众所周知的技术,它会导致意外的结果。 To see how it works, let's consider a simpler example that reproduces this problem: 为了查看其工作原理,让我们考虑一个重现此问题的简单示例:

valarray<int> a{1, 1};
a = a + a[0];
// now a is {2, 3} while {2, 2} is expected

The key idea of expression templates is to postpone the evaluation of expression until its value is really needed, such that extra temporary is avoided. 表达式模板的主要思想是将对表达式的评估推迟到真正需要它的值之前,这样可以避免额外的临时性。

In the example above, an optimizer may choose to optimize the result of a + a[0] to be a proxy object instead of a valarray<int> temporary. 在上面的示例中,优化器可以选择将a + a[0]的结果优化为代理对象,而不是临时valarray<int> The proxy object just stores the action (not the result value) of "adding a[0] to a ". 代理对象只是存储的动作 (不结果值)“加入a[0]a ”。

When the proxy object is then assigned to a , actual evaluation occurs. 当代理对象然后被分配到a时,发生实际的评价。 From the stored action, the optimizer will choose to assign a[i] + a[0] to a[i] for each i . 从存储的操作中,优化器将选择为每个i分配a[i] + a[0]a[i] Now different evaluation orders in this assignment will result in different results. 现在,此分配中的不同评估顺序将导致不同的结果。 For example, if the compiler assigns a[0] + a[0] to a[0] , and then assigns a[1] + a[0] (here a[0] is changed to 2) to a[1] , the unexpected result {2, 3} is produced. 例如,如果编译器将a[0] + a[0]分配给a[0] ,然后将a[1] + a[0] (此处a[0]更改为2)分配给a[1] ,将产生意外结果{2, 3}

The standard allows such proxy object to exist, but it seems not clearly to specify how the proxy object should work. 该标准允许此类代理对象存在,但似乎不清楚如何指定代理对象应如何工作。 I personally think this is a compiler bug, because simply evaluating a[0] and storing its value before assignment will solve this problem with little performance loss. 我个人认为这是一个编译器错误,因为只需评估a[0]并在赋值之前存储其值即可解决此问题,而性能损失很小。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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