[英]Why does as_const forbid rvalue arguments?
我想問一下,根據cppreference.com,為什么as_const
禁止 rvalue 參數(即為什么標准人員這樣做,而不是為什么 cppreference.com 專門引用了它們。也沒有在規范中將委員會的意圖編入規范) ,只是為了確保:)))。 這個(人工)示例會產生一個錯誤(用戶希望將其設置為 const 以保持 COW 安靜)
QChar c = as_const(getQString())[0];
另一個問題的答案指出,如果我們只是刪除右值引用重載的刪除,它會默默地將右值轉換為左值。 是的,但是為什么不優雅地處理右值並返回右值輸入的 const 右值和左值輸入的 const 左值呢?
問題是處理壽命延長
const auto& s = as_const(getQString()); // Create dangling pointer
QChar c = s[0]; // UB :-/
一種可能性是以下重載(而不是已刪除的重載)
template< typename T >
const T as_const(T&& t) noexcept(noexcept(T(std::forward<T>(t))))
{
return std::forward<T>(t);
}
這涉及額外的建設,也許還有其他陷阱。
一個原因可能是由於缺乏所有權轉移,右值可能很危險
for (auto const &&value : as_const(getQString())) // whoops!
{
}
並且可能沒有令人信服的用例來證明忽略這種可能性是合理的。
(在誤讀了這個問答的相關問題后,我不小心回答了錯誤的問題;我將我的答案移到這個問題上,我的答案實際上解決了這個問題)
P0007R1引入了std::as_const
作為 C++17 的一部分。 被接受的提案根本沒有提到右值,但它的先前修訂版P0007R0包含對右值的結束討論 [強調我的]:
九。 進一步討論
上面的實現只支持安全地將 l 值重新轉換為 const(即使它可能已經是 const)。 讓 xvalues 和 prvalues 也可用於 as_const 可能是可取的,但有一些問題需要考慮。
[...]
支持上述所有形式的替代實現是:
template< typename T > inline const T & as_const( const T& t ) noexcept { return t; } template< typename T > inline const T as_const( T &&t ) noexcept( noexcept( T( t ) ) ) { return t; }
我們相信這樣的實現有助於處理由 as_const 捕獲的臨時對象的生命周期延長問題,但我們還沒有完全檢查這些形式的所有含義。 我們願意擴大這個提議的范圍,但我們認為即使沒有擴展的語義,簡單易用的 as_const 的效用也足夠了。
因此, std::as_const
基本上只為std::as_const
添加,因為原始提案並未完全檢查為右值實現它的含義,即使至少訪問了右值參數的值重載返回。 另一方面,最終提案的重點是將實用程序用於左值的常見用例。
P2012R0旨在解決基於范圍的for循環的隱患
修復基於范圍的 for 循環 Rev0
基於范圍的 for 循環成為現代 C++ 最重要的控制結構。 它是處理容器/集合/范圍的所有元素的循環。
然而,由於它當前的定義方式,它很容易在普通應用程序程序員實現的非平凡但簡單的應用程序中引入生命周期問題。
[...]
症狀
在迭代集合元素的元素時,請考慮以下代碼示例:
std::vector<std::string> createStrings(); // forward declaration … for (std::string s : createStrings()) … // OK for (char c : createStrings().at(0)) … // UB (fatal runtime error)
雖然迭代臨時返回值工作正常,但迭代臨時返回值的引用是未定義的行為。
[...]
問題的根本原因
上面未定義行為的原因是,根據當前規范,循環內部的 range-base 擴展為多個語句:[...]
以及以下循環調用:
for (int i : createOptInts().value()) … // UB (fatal runtime error)
定義為等效於以下內容:
auto&& rg = createOptInts().value(); // doesn't extend lifetime of returned optional auto pos = rg.begin(); auto end = rg.end(); for ( ; pos != end; ++pos ) { int i = *pos; … }
按照規則,在引用
rg
初始化期間創建的所有未直接綁定到它的臨時值都在原始 for 循環開始之前被銷毀。[...]
問題的嚴重性
[...]
作為此問題引起的限制的另一個示例,請考慮在基於范圍的 for 循環中使用
std::as_const()
:std::vector vec; for (auto&& val : std::as_const(getVector())) { ... }
兩個
std::ranges
帶有operator |
和std::as_const()
有一個刪除的右值重載來禁用這個和類似的用途。 通過提議的修復,這樣的事情是可能的。 我們絕對可以討論此類示例的可用性,但似乎問題導致=delete
函數調用右值的示例比我們想象的要多。
這些陷阱是避免允許std::as_const()
的std::as_const()
重載的一個論據,但是如果 P2012R0 被接受,則可以說可以添加這樣的重載(如果有人提出建議並為其顯示有效用例)。
因為 as_const 不將參數作為常量引用。 非常量左值引用不能綁定到臨時對象。
顯式刪除轉發引用重載。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.