2012-04-03 47 views
12

This question提到了C++ 11基於範圍的顯而易見的習慣用法。適用於基於範圍的申報風格

for (auto& elem: container) { 
    // do something with elem 
} 

雖然我一直在懷疑你應該使用哪種引用。輸入迭代器可能會返回右值。儘管由auto引入的隱式類型可以推導爲const,它將綁定到右值,這似乎不會發生。

是使用完美轉發的最佳一般做法嗎?

for (auto && elem: container) { 
    // do something with elem 
} 

我沒有看到這裏的缺點,但它看起來有點可愛。也許我還沒有寫足夠的C++ 11。

+0

對於無法享受基於範圍的窮人MSVC10用戶,相同的問題適用於'BOOST_FOREACH'。 – 2012-04-03 13:59:49

+0

'auto'永遠不是'const'。你需要說'auto const&'。另外,'auto &&'不是右值引用,而更像是「通用引用」。 – 2012-04-03 14:56:45

+0

@KerrekSB在這裏沒有推導出'const'的好消息。我從來沒有說任何有關右值引用; v) – Potatoswatter 2012-04-03 15:03:22

回答

6

首先,一些關於如何使用auto的一般性建議,它不是特定於range-for的。 auto&&可能會有問題,如果初始化程序是一個xvalue引用臨時,因爲在這種情況下可能不會應用生命週期延長。要說得簡單些,用代碼:

// Pass-through identity function that doesn't construct objects 
template<typename T> 
T&& 
id(T&& t) 
{ return std::forward<T>(t); } 

// Ok, lifetime extended 
// T {} is a prvalue 
auto&& i = T {}; 

T* address = &i; 

// Still ok: lifetime of the object referred to by i exceed that of j 
// id(whatever) is an xvalue 
auto&& j = id(std::move(i)); 

// No other object is involved or were constructed, 
// all those references are bound to the same object 
assert(&j == address); 

// Oops, temporary expires at semi-colon 
// id(whatever) is an xvalue, again 
auto&& k = id(T {}); 

大線索,有一些陰暗的事情在這裏是id有返回類型T&&。如果它返回T然後id(whatever)將是一個prvalue,並且返回的臨時將延長其使用期限(但這將涉及一個構造)。


有了這樣的方式,當它涉及到的範圍,對於雖然你一定要記住,for(auto&& ref: init) { /* body */ }被指定爲大致相當於以下(忽略一些細節,不事這裏):

{ 
    using std::begin; 
    using std::end; 
    auto&& range = init; 
    for(auto b = begin(range), e = end(range); b != e; ++b) { 
     auto&& ref = *b; 
     /* body */ 
    } 
} 

我們要問自己,現在,如果*b是x值(即迭代器類型有一個operator*返回value_type&&,如同例如用std::move_iterator<Iterator>的情況下)?因此,它必須參考長出ref的對象,因爲行auto&& ref = *b;不涉及臨時。因此它是安全的。否則,如果*b是一個prvalue(即對於某些對象類型T,迭代器類型具有operator*返回T),則臨時的生命週期將在循環體的其餘部分進行擴展。在所有情況下,你都是安全的(*b是一個左值作爲練習讀者的情況)。

我個人大量使用auto&&,有或沒有range-for。但是我每次都會問自己,初始化器是否是一個xvalue,如果是,那麼被引用的是什麼時間。

+0

更多信息。另一個角度......函數可以返回一個xvalue的唯一臨時值是一個右值參數。 'begin'和'end'沒有參數,因此轉發他們的結果是安全的。 – Potatoswatter 2012-04-04 02:27:52

+0

@Patatoswatter在這個例子中,它是'* b',它是感興趣的(並且確實沒有參數,更不用說您正確分析的臨時參數)。 'begin'和'end'確實需要參數,但它們只能用於左值。 – 2012-04-04 03:05:09

+0

是的,我指的是'begin'和'end' *成員*,不包括'this'因爲它是一個左值。 'operator *'與「begin」和「end」具有基本相同的意義。但是,這是真正重要的一個。 – Potatoswatter 2012-04-04 03:27:51