[英]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];
的類型的out
是BinOpPlus< 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.