現代C++核心指導中提到的 span<T> 類型到底是哪個?
CppCoreGuidelines/CppCoreGuidelines.md at master · isocpp/CppCoreGuidelines · GitHub
CppCoreGuidelines 作者 Bjarne Stroustrup
【最後有高能預警】這個不是標準庫里的。是GSL里的,也就是那個指導手冊(CppCoreGuidelines)的配套庫Guideline support library。封裝了這個指導手冊里的一些條款所涉及的技巧。看這個手冊可以看到,span表示的是容器範圍(range),STL的很多演算法都是針對範圍操作的,此時這樣演算法需要的參數往往是首尾迭代器。另外一些其他的針對範圍操作的函數/成員函數需要的參數是首地址,個長度。無論哪種都容易造成在不合法的範圍內操作。span(應該是個模板類)則會進行安全檢查。-------------------------------------------------補充------------------------------------------------
這個問題,前面回答是我看了那個指導手冊(F.24節)後,自己的理解。所以怕有誤。谷歌無果之後,所以提到Quora上(What is the span T in the CppCoreGuidelines?)。
然後Bjarne Stroustrup 親自回答了這個問題(為國外老程序員的敬業默默點贊)。奉上原文:Span&
is introduced into the GSL, not the STL, though it might eventually be proposed for that. It is a non-owning range of contiguous memory intended as an alternative to the error-prone (pointer,count) interfaces. The GSL is here: Microsoft/GSL (its open source code (BSD license), hosted by Microsoft, but in no way Microsoft specific).
大意就是,這個GSL庫目前不是標準庫里的,但是最終目的是併入標準庫。微軟在Github上有GSL庫的開源代碼。地址:Microsoft/GSL
GSL庫裡面的類:GSL/span.h at master · Microsoft/GSL · GitHub
span&
span&
- 解決傳參時數組退化所導致的丟失數組尺寸的問題
- 更容易地檢查出因為範圍(ranges)大小錯誤而產生的問題
-------------------------------------------------------------------------------------------------
舉幾個例子:1. 給定一個int數組,如何設計一個函數為數組的所有元素+1?
(來自C++ Core Guidelines 條款P.7)void increment1(int* p, int n) // 容易出錯
{
for (int i = 0; i &< n; ++i)
++p[i];
}
void use1(int m)
{
const int n = 10;
int a[n] = {};
// ...
increment1(a, m); // 不慎將 n 錯打為 m,或者設計者希望 m &<= n
// 如果在 m &> n 的情況下調用increment1呢?
// ...
}
在這種情況下,只有在increment1訪問p[10]時,錯誤才有可能產生。
這種情況下就可以使用span&void increment2(span&
{
for (int x : p)
++x;
}
void use2(int m)
{
const int n = 10;
int a[n] = {};
// ...
increment2({a, m}); // 不慎將 n 錯打為 m,或者設計者希望 m &<= n
// ...
}
這樣就能保證編譯器在發現 m &> n 的時候就能報錯。
而且如果設計者希望increment作用於所有元素,可以這樣:void use3(int m)
{
const int n = 10;
int a[n] = {};
// ...
increment2(a); // 無須指出元素個數
// ...
}
-------------------------------------------------------------------------------------------------
2. 如何將一個數組裡面的若干個元素複製到另一個數組裡?(來自C++ Core Guidelines 條款I.13)你可能會這樣做:template &
void copy_n(const T* p, T* q, int n); // 從 p 里複製 n 個元素到 q
但是這樣做存在兩個問題:
- 如果 q 的元素個數比 n 小呢?這樣有部分無關內存會被重寫。
- 如果 p 的元素個數比 n 小呢?這樣有部分無關內存會被讀取。
任何一個問題都是未定義行為,而且是一個潛在的Bug。
這時就可以使用span:template &
void copy(span&
-------------------------------------------------------------------------------------------------
3. 有一個Circle數組,如何使用一個draw函數列印所有的Circle對象?(來自C++ Core Guidelines 條款I.13)class Shape { /* ... */ };
class Circle : public Shape { /* ... */ }
void draw(Shape* p, int n); // 參數是 Shape* 而不是 Circle*
Circle arr[10];
// ...
draw(arr, 10); // 調用了參數為Shape* 的函數,可能不是調用者的本意
這種調用有兩個問題:
- 無法確定 n 是否應該是 p 的大小。(雖然有很大可能,但函數聲明無法保證這一點)
- Circle[10] 先被隱式地轉換成了 Circle* ,又被轉換成了 Shape* ,沒有任何警告。
在這種情況下使用span可以預防這樣的隱式轉換:
void draw2(span&
Circle arr[10];
// ...
draw2(span&
draw2(arr); // 自動推導元素類型與個數
void draw3(span& -------------------------------------------------------------------------------------------------
draw3(arr); // 錯誤:無法將 Circle[10] 轉換為 span&
X* find(span&
vector& span對象顯式地表示了一段區間,它並不具有這段區間元素的所有權,可以按值傳遞。
// ...
auto p = find({vec.begin(), vec.end()}, X{}); // 在vec里搜尋值為 X{} 的元素
沒找到你說的span,先看看在stl庫里沒有,如果不在就搜一搜微軟提供的幫助庫參考實現,在ithub上,整個guideline都是利用stl和幫助庫來做的,沒有其他依賴。
C++作者親自為你解答
推薦閱讀:
※C++ 為什麼有時候必須額外寫 template?
※C++ 模版元編程對快速開發原型有何幫助?
※c++中虛析構函數如何實現多態的、內存布局如何?
※求一本或者幾本比較詳細介紹C++11的書,《C++ Primer》是否合適?
※看見網上說學單片機有助於c++的學習,是這樣的嗎?