[英]With new move semantics, when should you not use a reference in scope?
考慮以下代碼,其中foo
的簽名為some_type_t foo();
auto const result = foo(); // copy
auto const & result = foo();
auto const && result = foo();
在這種情況下,使用auto const
將是一個浪費的副本。 第二種和第三種情況是無需額外副本即可訪問從foo
返回的數據的唯一方法。 第二個和第三個是一樣的。
這是另一個片段
auto result = foo(); // copy
auto & result = foo(); // error
auto && result = foo();
在這個片段中,第二種情況不會編譯,因為無法將右值綁定到左值引用。 第一種情況復制結果,而第三種情況直接從foo
“獲取”返回值而不進行復制。
考慮到這一點,在聲明范圍內的變量時,您什么時候不會有&
? 似乎,只要您始終使用std::move(result)
訪問結果,擁有參考總是更有益。
與問題的前提相反,這些都不會導致復制或移動:
auto const result = foo(); // (1)
auto const & result = foo(); // (2)
auto const && result = foo(); // (3)
auto result = foo(); // (4)
auto & result = foo(); // (5) error
auto && result = foo(); // (6)
那么有哪些優點/缺點?
(2), (3) 和 (6) 使用生命周期擴展來確保foo()
的結果足夠長以供使用。 這在非常簡單的重構下是脆弱的:
auto const & result = std::max(foo(), 1); // compiles, but broken
(3) 和 (6) 依賴於返回值的臨時性質,所以在某種程度上,它們打破了foo
封裝。 我將解釋我的意思。 想象一下foo
很昂貴,而您希望將值緩存在某處。 然后你會改變foo()
以返回一個const some_type_t&
。 這些選項之間的選擇可以看作是foo
的實現細節。 它如何改變初始化:(1)和(4)仍然有效但引入副本,(2)工作相同,並且(3)和(6)不再編譯。
因此,人們必須在某些變化(2)(3)和(6)下靜默破碎,在某些變化(3)和(6)下大聲破碎,以及在變化(1)和(4)下從未破碎但可能更慢之間做出決定. 這取決於正在構建的系統的優先級。 可以想象在系統中,不破壞現有的正確代碼比在重構時避免無聲的額外副本更重要。 在這樣的系統中,(1) 和 (4) 可能是首選。
或者,可以從這個初始化站點的代碼告訴我們的內容的角度來處理它。 如果代碼審查是您的開發過程的一部分,那么這種觀點將更加相關。 (2), (3) 和 (6) 沒有告訴我們關於結果引用的生命周期,因為foo()
可能返回一個引用。 所以我們需要更多的上下文。 但是(1)和(4)是明確的關於生命周期的,所以它們給了我們更多的信息。
它是 C++。 我們有很多初始化和資源生命周期的選項,它們都有權衡。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.