std::span 與 std::string_view(非擁有視圖)
想像你想寫個處理位元組序列的函式,而你希望它不論位元組來自 std::vector、C 陣列、std::string 還是裸 malloc 出的緩衝區都能運作——既不複製它們,也不逼呼叫者用某一種容器型別。視圖(view)解決了這點:它是個小物件,只持有指向既有資料的指標加上一個長度,借用那份資料卻不擁有它。std::span 是對任意元素型別之連續序列的視圖;std::string_view 是對字元序列的特化視圖。
具體而言,std::span<T> 本質上是個(指標, 數量)對:它不配置、不複製、也不釋放任何東西——它只是描述一扇望向別處記憶體的窗。把 std::vector 或 C 陣列傳給接受 std::span<int> 的函式,函式就在原地看到相同的元素。std::string_view 是對文字的同一想法:它取代了傳遞 const char*(沒有長度)或 const std::string&(強制用 std::string,可能複製)的舊習慣,改用任何類字串來源都能廉價產生的統一(指標, 長度)視圖。因為它們不擁有,兩者傳遞起來都極小,並把「只在我的容器上能用」的函式變成「在任何連續資料上都能用」的函式。
為何重要:視圖是零開銷原則的乾淨展現——你得到一個有彈性的介面,沒有複製、沒有配置。誠實且關鍵的提醒是生存期:視圖不會讓它的底層資料存活,所以它只在那份資料還在時才有效。指向暫存 std::string 的 std::string_view,或指向某個之後重新配置(或離開範圍)之 std::vector 的 std::span,都會變成懸置視圖——一次讀取已釋放或已移動記憶體的釋放後使用。視圖是帶邊界的借用指標;請以你對待裸指標的同等生存期紀律對待它們。
void sum(std::span<const int> xs); std::vector<int> v{1,2,3}; sum(v); int a[]{4,5}; sum(a); // 一個函式,零複製,兩者皆可用
span 從任何連續來源借用(指標 + 長度);同一個函式接受 vector 與 C 陣列,兩者都不複製。
視圖永不延長它所指向之物的生存期:從一個建立了暫存 std::string 的函式回傳的 string_view 立刻懸置,而指向 vector 的 span 在那個 vector 因成長而重新配置的那一刻就失效。活得比資料久就是釋放後使用,就這麼簡單。