2017-04-25 26 views
1

最近我的程序中出現了一個bug,這讓我感到吃驚,但也許它不應該,C++(特別是現代C++)提供了大量的初始化類型。我有一個類,它看起來是這樣的:這是一個支撐初始化列表的不安全用法?

struct foo 
{ 
    foo(const std::set<int> &values) { values_ = values; } 

    set::set<int> values_; 
}; 

在構建foo S,它通常是最容易指定一組內嵌使用初始化列表值(一個實例通常會有2-3個已知值已知在編譯時),就像這樣:

auto f = std::make_shared<foo>({ 1, 2, 3 }); 

不過,我記得,當我第一次寫作課foo,上述用法給我的編譯時間問題;我不記得確切的錯誤,但我發現,明確調用initializer_list構造允許編譯器正確推斷如何處理這些參數,就像這樣:

auto f = std::make_shared<foo>(std::initializer_list<int>({ 1, 2, 3 })); 

這種「工作」很長一段時間,但我的應用程序最近遇到了一些模糊不清的隨機崩潰。調試後,我們發現,改變上述給:

auto f = std::make_shared<foo>(std::initializer_list<int>{ 1, 2, 3 }); 

(即,從副本初始化改變到支撐初始化)固定的問題。這使我想到這個問題:

上面的示例中顯示的初始化程序列表的複製構造是不安全的做法嗎?支撐初始值設定項中值的生存期是否與全封閉表達式的生命週期不匹配?

這可能只是一些其他的,仍然存在的錯誤的症狀,或某種編譯器錯誤(我正在使用Apple clang v8.0)。

+0

請顯示您的構造函數定義。 –

+0

@KerrekSB:已添加。我所做的只是複製作爲參數傳入的'set'。這是一個非常簡化的版本;啓發'foo'的實際班級要複雜得多。但是,在'set'上完成的唯一操作如示例中所示。 –

+3

您遇到的錯誤與此處的初始化無關。通過一些動態消毒劑(Valgrind,ASAN,TSAN)運行你的程序來找到真正的錯誤。 –

回答

1

臨時的初始化列表應該持續到它們被構造的語句的結尾,並且它們的生命期延長了它們包裝的數組。

雖然make_shared<?>({1,2,3})預計不會工作(請參閱完美轉發的缺陷),但其餘代碼應該。如果您所描述的更改實際上導致代碼開始/停止工作,則會導致內存損壞。

如果這是確定性的概率很高,問題可能是某人對堆棧上的對象有一個懸掛引用,並且正在修改或讀取它。當您更改構造初始化程序列表的方式時,堆棧中對象的佈局會發生變化,並且會發生不同的行爲(某些值非零或零,並且分支走向不同,或者寫入的內容無關緊要在一種情況下,另一種情況下)。

如果是基於寫入的內存損壞,則將內存斷點和跟蹤對堆棧區域的所有訪問權限(儘可能繁瑣)有時可能會發現跳轉指針所在的位置。

相關問題