簡體   English   中英

帶有O3的令人困惑的表達模板段錯誤

[英]Befuddling Expression Template Segfault with O3

在我的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 ,顯示錯誤的程度還不夠。 有人知道我在做什么錯嗎?

在這條線

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

的類型的outBinOpPlus< BinOpPlus<ValArray, ValArray>, Valarray> 由於您的BinOpPlus存儲了對其參數的引用,並且BinOpPlus<ValArray,ValArray>存在臨時狀態,因此您具有未定義的行為。

通常,此類表達式模板使用特征來決定如何存儲其參數,以便您可以按引用存儲實際對象(並假設用戶不會搞亂),而按值存儲其他ET(無論如何它們都很小)。

還將auto與算術ET一起使用至少被認為是有問題的,因為它很少產生預期的類型。 出於這個原因,已經提出了一些建議,以引入一種operator auto來自定義ET中的auto推斷的類型。

這是預感。

在行中

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

您有臨時對象。 可能會使用-O3標志將其刪除。 我會嘗試以下方法:

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

更改代碼以在結構中不使用引用成員。 我相信當您在此處執行添加操作時,參考成員會使復制操作陷入困境:

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

例如:

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

進行此更改可以正常工作。

此外,僅供參考,當使用帶有完整警告( /W4 )的Visual Studio 2013編譯代碼時,我們會收到以下警告:

警告C4512:“ BinOpPlus”:無法生成賦值運算符。

如此看來,這表明任何復制都可能產生不良影響。


沒有參考的良好運行的實時示例: http//ideone.com/JKxoDv

帶有參考的不良運行的實時示例: http : //ideone.com/7oSoJB

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM