0

在我的 gcc-4.8.1 上,我使用两个命令编译了以下程序:

g++ -Wfatal-errors -std=c++11 -Wall -Werror test.cpp -o test -g
g++ -Wfatal-errors -std=c++11 -Wall -Werror test.cpp -o test -O3 -g

第一个可执行文件具有预期的输出,但第二个可执行文件有段错误。问题是很难调试,因为-O3与代码混淆太多以至于-g调试信息无法保留意义,因此gdb在翻译源代码中发生的事情时遇到了麻烦。所以,我开始插入打印语句。正如我所料,打印语句改变了结果。使用调试打印,它工作得很好!

这是我的表达式模板源:

//test.cpp
#include <vector>
#include <stdlib.h>
#include <iostream>

using namespace std;

typedef vector<int> Valarray;

template<typename L, typename R>
struct BinOpPlus {
  const L& left;
  const R& right;

  BinOpPlus(const L& l, const R& r)
    : left(l), right(r)
  {}

  int operator[](int i) const { 
    int l = left[i];
    //cerr << "Left: " << l << endl; //uncomment to fix segfault
    int r = right[i];
    //cerr << "Right: " << r << endl; //uncomment to fix segfault
    return l + r;
  }
};

template<typename L, typename R>
BinOpPlus<L, R> operator+(const L& left, const R& right){
  return BinOpPlus<L, R>(left, right);
}

int main() {
  //int size = 10000000;
  int size = 10;
  Valarray v[3];
  for(int n=0; n<3; ++n){
    for(int i=0; i<size; ++i){
      int val = rand() % 100;
      v[n].push_back(val);
    }
  }

  auto out = v[0] + v[1] + v[2];

  int sum = 0;
  for(int i=0; i<size; ++i){
    cerr << "Checkpoint!" << endl;
    sum += out[i]; //segfaults here
    cerr << "Sum: " << sum << endl;
  }

  cout << "Sum: " << sum << endl;
  return 0;
}

很久-O3没有给我一个不正确/不可靠的二进制文件了。我首先假设我在代码中做错了什么,但还不足以-O0显示它。有人知道我做错了什么吗?

4

3 回答 3

1

在这一行

auto out = v[0] + v[1] + v[2];

的类型outBinOpPlus< BinOpPlus<ValArray, ValArray>, Valarray>。由于您BinOpPlus存储了对其参数的引用,并且BinOpPlus<ValArray,ValArray>存在临时性,因此您的行为未定义。

通常像这样的表达式模板使用一个特征来决定如何存储它们的参数,这样你就可以通过引用存储实际对象(并假设用户不会搞砸)和其他 ET 值(无论如何它们都非常小)。

auto认为与算术 ET 一起使用至少是有问题的,因为它很少产生预期的类型。出于这个原因,已经有一些提议要引入一种operator auto自定义 ET 中由 auto 推断的类型。

于 2015-02-03T23:19:59.200 回答
0

Change your code to not use reference members in your struct. I believe that the reference members makes the copy operation go haywire when you do the addition here:

auto out = v[0] + v[1] + v[2];

For example:

template<typename L, typename R>
struct BinOpPlus {
  const L left;
  const R right;

Making this change works correctly.

Also, FYI, when compiling your code with Visual Studio 2013 with full warnings (/W4), we get this warning:

warning C4512: 'BinOpPlus' : assignment operator could not be generated.

So right there, this indicates that any copying may produce undesired effects.


Live example of good run without references: http://ideone.com/JKxoDv

Live example of bad run with references: http://ideone.com/7oSoJB

于 2015-02-03T23:25:29.267 回答
0

这是一种预感。

在行

auto out = v[0] + v[1] + v[2];

你有临时对象。这可能会被-O3标志删除。我会尝试以下方法:

auto out1 = v[1] + v[2];
auto out = v[0] + out1;
于 2015-02-03T23:07:45.120 回答