天天看點

C++20 span

C++20的span

    • 01 範圍檢查:span
    • 02 span demo

01 範圍檢查:span

span是對象的連續序列上的無所有權視圖。1

類模闆 span 所描述的對象能指代對象的相接序列,序列的首元素在零位置。 span 能擁有靜态長度,該情況下序列中的元素數已知并編碼于類型中,或擁有動态長度。

典型實作隻保有二個成員:指向 T 的指針和大小。2

到2020年1月16日前,有3個span的開源版本。

微軟實作了一個版本的span3。微軟版gsl目前有9個頭檔案。

獨立頭檔案的相容C ++ 11及更高版本的span實作。4

span lite:适用于C ++ 98,C ++ 11和更高版本的C ++ 20跨度的單檔案标頭版本。5

span用來做範圍檢查。提供對一個連續元素序列(主要指内置數組)的通路能力。元素可用很多方式存儲,包括存儲在vector和内置數組中。類似于指針,一個span不擁有它指向的字元。在這一點上,它很像string_view和STL的疊代器對。

std::span可能采用合約來控制對範圍錯誤的響應。span的範圍檢查幾乎無額外性能代價。6

span能很好的解決内置數組退化和越界通路的問題。7

02 span demo

https://github.com/5455945/cpp_demo/blob/master/C%2B%2B20/span/span.cpp

#include <iostream>
// 這裡的gsl::span是微軟的一個開源實作
// https://github.com/microsoft/GSL/tree/master/include/gsl
#include <gsl/gsl>

using namespace std;

// 《C++語言導論》 P147 13.3 範圍檢查:span
void fpn(int* p, int n) { // p可以是一個内置數組
    for (int i = 0; i < n; i++) {
        p[i] = 1;
    }
}

void use(int x) {
    int a[100];  // 内置數組
    fpn(a, 100);       // 正确
    //fpn(a, 1000);      // 糟糕,範圍越界,(0xC0000005: 寫入位置 0x000000E546F00000 時發生通路沖突。)
    //fpn(a + 10, 100);  // fpn中産生範圍錯誤,(Run-Time Check Failure #2 - Stack around the variable 'a' was corrupted.)
    //fpn(a, x);         // 可疑的,但看起來無事,(x>100,Run-Time Check Failure #2 - Stack around the variable 'a' was corrupted.)
}

void fs(gsl::span<int> p) {
    for (auto& x : p) {
        x = 2;
    }
}

void use_span(int x) {
    int a[100];
    fs(a);               // 隐式建立一個span<int>{a, 100}
    //fs({ a, 1000 });     // 錯誤:期待一個sapn
    //fs({ a + 10, 100 }); // 在fs中發生範圍錯誤,(Run-Time Check Failure #2 - Stack around the variable 'a' was corrupted.)
    //fs({ a, x });        // 明顯可疑,(x>100,Run-Time Check Failure #2 - Stack around the variable 'a' was corrupted.)
}

// C++核心準則邊譯邊學-X.4:使用span解決數組退化和越界通路
// https://blog.csdn.net/craftsman1970/article/details/103217292
void traditional_array(int buffer[], size_t size)
{
    cout << "size=" << size << endl;
    for (int i = 0; i < int(size); ++i) {
        buffer[i] = i;
    }
    buffer[0] = int(size * 2);
    //buffer[size * 2] = int(size * 4);  // 傳統數組越界通路
}

void span_array(gsl::span<int> buffer)
{
    cout << "size=" << buffer.size() << endl;
    int value = 0;
    for (auto it = buffer.begin(); it != buffer.end(); it++) {
        *it = value++;
    }
    buffer[0] = value++;
    //buffer[buffer.size() * 2] = value++; // span會觸發斷言
}

int main()
{
    int x = 105;
    use(x);
    use_span(x);

    int data[10]; // 内置數組
    for (size_t i = 0; i < sizeof(data) / sizeof(data[0]); ++i) {
        data[i] = 0;
    }
    // 使用數組傳遞參數
    traditional_array(data, 5);
    // 使用span傳遞參數
    span_array(data);

    gsl::span<int> sdata = data;
    for (auto v : sdata) {
        std::cout << v << ", ";
    }
    std::cout << std::endl;

    return 0;
}
           
  1. 标準庫頭檔案span ↩︎
  2. std::span ↩︎
  3. 準則支援庫 ↩︎
  4. 為舊版編譯器實作C ++ 20的單獨頭檔案的span ↩︎
  5. span lite,适用于C++98,及更高版本的單獨頭檔案實作的span ↩︎
  6. 《C++語言導論》第二版 P147 ↩︎
  7. C++核心準則邊譯邊學-X.4:使用span解決數組退化和越界通路 ↩︎

繼續閱讀