標籤:

現代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&說的是[begin, end)這樣一個range,比如說常見memcpy(dest, src, size)這樣的函數,其中src和size等價於[src, src+size),就是這樣一個range。

BS在比較一些常用的pattern,他的意思是說,如果由他來寫,他會用memcpy(dest, src_begin, src_end)這樣的interface而不是現有的memcpy(dest, src, size)。


span&就是在CppCon 2015裡面提到的array_view&的新名稱,它的主要作用有兩個:

  • 解決傳參時數組退化所導致的丟失數組尺寸的問題
  • 更容易地檢查出因為範圍(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& p) // 改用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& p, span& r); // 複製 p 的所有元素到 q

-------------------------------------------------------------------------------------------------

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* 的函數,可能不是調用者的本意

這種調用有兩個問題:

  1. 無法確定 n 是否應該是 p 的大小。(雖然有很大可能,但函數聲明無法保證這一點)
  2. Circle[10] 先被隱式地轉換成了 Circle* ,又被轉換成了 Shape* ,沒有任何警告。

在這種情況下使用span可以預防這樣的隱式轉換:

void draw2(span&);
Circle arr[10];
// ...
draw2(span&(arr)); // 自動推導元素個數
draw2(arr); // 自動推導元素類型與個數

void draw3(span&);
draw3(arr); // 錯誤:無法將 Circle[10] 轉換為 span&

-------------------------------------------------------------------------------------------------

4. 如何從任意一段區間(元素類型為X)搜索某個X類特定值?

(來自C++ Core Guidelines 條款F.24)

X* find(span& r, const X v); // 從 r 里搜尋 v

vector& vec;
// ...
auto p = find({vec.begin(), vec.end()}, X{}); // 在vec里搜尋值為 X{} 的元素

span對象顯式地表示了一段區間,它並不具有這段區間元素的所有權,可以按值傳遞。

-------------------------------------------------------------------------------------------------

最後放上微軟的span實現:GSL/span.h at master · Microsoft/GSL · GitHub


沒找到你說的span,先看看在stl庫里沒有,如果不在就搜一搜微軟提供的幫助庫參考實現,在ithub上,整個guideline都是利用stl和幫助庫來做的,沒有其他依賴。


C++作者親自為你解答


推薦閱讀:

C++ 為什麼有時候必須額外寫 template?
C++ 模版元編程對快速開發原型有何幫助?
c++中虛析構函數如何實現多態的、內存布局如何?
求一本或者幾本比較詳細介紹C++11的書,《C++ Primer》是否合適?
看見網上說學單片機有助於c++的學習,是這樣的嗎?

TAG:C | C11 | C14 |