簡體   English   中英

std :: begin和R-values

[英]std::begin and R-values

最近我試圖修復一個相當困難的const-correctness編譯器錯誤。 它最初表現為Boost.Python內部的多段模板嘔吐錯誤。

但這是無關緊要的:它都歸結為以下事實:C ++ 11 std::beginstd::end迭代器函數不會重載以獲取R值。

std::begin的定義是:

template< class C >
auto begin( C& c ) -> decltype(c.begin());

template< class C >
auto begin( const C& c ) -> decltype(c.begin());

因此,由於沒有R值/通用引用重載,如果您傳遞一個R值,您將獲得一個const迭代器。

那我為什么要關心? 好吧,如果您有某種“范圍”容器類型,即像“視圖”,“代理”或“切片”或某些容器類型,它們呈現另一個容器的子迭代器范圍,通常非常方便使用R值語義並從臨時切片/范圍對象中獲取非const迭代器。 但是使用std::begin ,你運氣不好,因為std::begin將始終為R值返回一個const-iterator。 這是一個老問題,C ++ 03程序員經常在C ++ 11給我們R值的前一天感到沮喪 - 即臨時問題總是綁定為const

那么,為什么不將std::begin定義為:

template <class C>
auto begin(C&& c) -> decltype(c.begin());

這樣,如果c是常量,我們得到一個C::const_iterator和一個C::iterator

起初,我認為原因是為了安全。 如果你將一個臨時文件傳遞給std::begin ,就像這樣:

auto it = std::begin(std::string("temporary string")); // never do this

...你得到一個無效的迭代器。 但后來我意識到這個問題仍然存在於當前的實現中。 上面的代碼只會返回一個無效的const -iterator,在解除引用時可能會出現段錯誤。

那么,為什么std::begin 沒有被定義為采用R值(或更准確地說,是通用引用 )? 為什么有兩個重載(一個用於const ,一個用於non-const )?

上面的代碼只會返回一個無效的const-iterator

不完全的。 迭代器將一直有效,直到迭代器引用的臨時表達式的完整表達式的結尾為止。所以類似於

std::copy_n( std::begin(std::string("Hallo")), 2,
             std::ostreambuf_iterator<char>(std::cout) );

仍然是有效的代碼。 當然,在您的示例中, it在語句末尾無效。

修改臨時或xvalue會有什么意義? 這可能是范圍訪問者的設計者在提出聲明時所考慮的問題之一。 他們沒有考慮.begin().end()返回的迭代器在其生命周期內有效的“代理”范圍; 也許是因為在模板代碼中,它們無法與正常范圍區分開來 - 我們當然不希望修改臨時非代理范圍,因為這是毫無意義的,可能會導致混淆。

但是,您不需要首先使用std::begin ,而是可以使用using-declaration聲明它們:

using std::begin;
using std::end;

並使用ADL。 這樣就可以為Boost.Python(os)使用的類型聲明命名空間范圍的beginend重載,並規避std::begin的限制。 例如

iterator begin(boost_slice&& s) { return s.begin(); }
iterator end  (boost_slice&& s) { return s.end()  ; }

// […]

begin(some_slice) // Calls the global overload, returns non-const iterator

為什么有兩個重載(一個用於const,一個用於非const)?

因為我們仍然希望支持rvalues對象(並且它們不能被T&形式的函數參數占用)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM