[英]Why does std::cbegin() not call .cbegin() on the container?
以下代碼未通過靜態斷言:
#include <gsl/span>
#include <iterator>
#include <type_traits>
int main()
{
int theArr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
gsl::span<int> theSpan{ theArr, std::size(theArr) };
using std::cbegin;
auto it1 = cbegin(theSpan);
auto it2 = theSpan.cbegin();
static_assert(std::is_same_v<decltype(it1), decltype(it2)>);
}
這失敗是因為std::cbegin()
在容器的const ref上調用.begin()
方法。 對於標准定義的容器,這將返回一個const_iterator
,它與.cbegin()
返回的類型相同。 但是, gsl::span
有點獨特,因為它模擬了一種“借用類型”。 const gsl::span
行為類似於const指針; span本身是const,但它指向的不是const。 因此, const gsl::span
上的.begin()
方法仍然返回一個非const迭代器,而顯式調用.cbegin()
返回一個const迭代器。
我很好奇為什么std::cbegin()
沒有被定義為在容器上調用.cbegin()
(所有標准容器似乎都要實現)來解決這種情況。
這與以下內容有些相關: 為什么std :: cbegin返回與std :: begin相同的類型
這失敗,因為
std::cbegin()
調用.begin()
更確切地說, std::cbegin
調用std::begin
,它在泛型重載中調用c.begin
。
對於它的價值,應該可以修復gsl::span
以在std::cbegin
上返回const迭代器,如果gsl的設計者指定對於gsl::span
的std::cbegin
的泛型重載有一個特殊化,那么如果這是所需的行為,則使用c.cbegin
而不是std::begin
。 我不知道他們沒有指定這種專業化的理由。
至於為什么std::cbegin
使用std::begin
,我也不知道,但它確實具有能夠支持具有c.begin
成員但不是c.cbegin
成員的容器的優點。 ,可以看作是一個不太嚴格的要求,因為它可以通過在C ++ 11之前編寫的自定義容器來滿足,當時沒有提供c.cbegin
成員函數的約定。
首先,請注意,根據[tab:container.req] :
表達式:
a.cbegin()
返回類型:
const_iterator
操作語義:
const_cast<X const&>(a).begin();
復雜性:不變
因此, gsl::span
根本不是容器。 cbegin
和cend
被設計成集裝箱的工作。 有一些例外(數組, initializer_list
)需要特別小心,但顯然標准庫不能提到像gsl::span
這樣的東西。
其次,它是LWG 2128介紹了全球cbegin
和cend
。 讓我們看看相關部分的內容:
通過調用
std::begin/end()
實現std::cbegin/cend()
std::begin/end()
。 這有很多好處:
它自動適用於數組,這是這些非成員函數的重點。
它適用於在
cbegin/cend()
成員發明之前編寫的C ++cbegin/cend()
時代用戶容器。它適用於
initializer_list
,它非常小,缺少cbegin/cend()
成員。[container.requirements.general]保證這相當於調用
cbegin/cend()
成員。
本質上,調用std::begin/end()
節省為數組和initializer_list
提供特殊維護的工作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.