2012-04-14 63 views
18

我知道一般情況下,基於範圍的for循環中的臨時對象的生命期延長至整個循環(我讀過C++11: The range-based for statement: "range-init" lifetime?)。像這樣的所以做的東西一般是OK:基於範圍的臨時對象

for (auto &thingy : func_that_returns_eg_a_vector()) 
    std::cout << thingy; 

現在我跌跌撞撞關於內存的問題時,我嘗試做一些我認爲是與Qt的QList容器相似:

#include <iostream> 
#include <QList> 

int main() { 
    for (auto i : QList<int>{} << 1 << 2 << 3) 
    std::cout << i << std::endl; 
    return 0; 
} 

這裏的問題valgrind在QList類的某處顯示無效的內存訪問。然而,修改的例子,這樣的列表存儲在變量提供一個正確的結果:

#include <iostream> 
#include <QList> 

int main() { 
    auto things = QList<int>{} << 1 << 2 << 3; 
    for (auto i : things) 
    std::cout << i << std::endl; 
    return 0; 
} 

現在我的問題是:我在做什麼,導致如第一種情況下啞未定義的行爲(我沒有足夠的經驗閱讀C++標準來爲自己回答這個問題)?或者這是我如何使用QList,或如何實現QList的問題?

回答

12

由於您使用的是C++ 11,you could use initialization list instead。這將通過Valgrind的:基於範圍

int main() { 
    for (auto i : QList<int>{1, 2, 3}) 
    std::cout << i << std::endl; 
    return 0; 
} 

問題是不完全涉及用於甚至C++ 11。下面的代碼說明了同樣的問題:

QList<int>& things = QList<int>() << 1; 
things.end(); 

或:

#include <iostream> 

struct S { 
    int* x; 

    S() { x = NULL; } 
    ~S() { delete x; } 

    S& foo(int y) { 
     x = new int(y); 
     return *this; 
    } 
}; 

int main() { 
    S& things = S().foo(2); 
    std::cout << *things.x << std::endl; 
    return 0; 
} 

無效讀取是因爲從表達S()(或QList<int>{})的臨時對象被聲明(下面的C後破壞++ 03和C++ 11§12.2/ 5),因爲編譯器不知道方法foo()(或operator<<)將返回該臨時對象。所以你現在引用釋放內存的內容。

+0

感謝您的澄清。而愚蠢的我,當然我應該首先使用一個初始化列表 - 我不知何故根本沒有考慮它。可能是由於Qt的例子總是在類似的情況下使用'<<'。 – 2012-04-14 13:03:43

+0

好吧,看起來像C++ 11支持僅在Qt 4.8和更高版本中可用。但對於這種情況,我可以輕鬆地使用標準庫中的容器。 – 2012-04-14 13:15:52

+0

這個問題是否可以通過轉換爲'QList const&'(即:寫'for(auto i:static_cast const&>(QList {} << 1 << 2 << 3))')?這樣,如果我正確讀取了6.5.4節,並且反過來將臨時的生命週期擴展到循環範圍,它將被綁定到'for'循環初始化中的const引用。 – 2012-04-14 13:16:04

6

編譯器不可能知道對operator <<的三次調用的結果的引用綁定到臨時對象QList<int>{},因此臨時對象的壽命不會延長。編譯器不知道(也不能期望知道)關於函數返回值的任何信息,除了它的類型。如果它是一個參考,它不知道它可能綁定什麼。我非常肯定,爲了延長壽命規則,綁定必須是直接的。

這應該工作,因爲名單不再是一個暫時的:

#include <iostream> 
#include <QList> 

int main() { 
    auto things = QList<int>{}; 
    for (auto i : things << 1 << 2 << 3) 
    std::cout << i << std::endl; 
    return 0; 
} 

,這應該工作,因爲結合是直接的,因此規則可以適用:

#include <iostream> 
#include <QList> 

int main() { 
    for (auto i : QList<int>{1, 2, 3}) 
    std::cout << i << std::endl; 
    return 0; 
}