输入关键词开始搜索

C++ string_view 与 span

string_view — 零拷贝的字符串”窗口”

#include <string_view>

// 不拥有数据,只是"看"一段字符
string_view sv = "hello";               // C 字符串
string s = "world";
string_view sv2 = s;                    // std::string
string_view sv3 = s.substr(0, 3);      // "wor" — 不分配新内存!

// 对比:string::substr 会产生新 string + 堆分配
string sub = s.substr(0, 3);            // 分配 + 拷贝

核心操作

string_view sv = "hello world";

sv.size();       // 11
sv.empty();      // false
sv[0];           // 'h'
sv.front();      // 'h'
sv.back();       // 'd'

// 子视图(O(1),不分配)
sv.substr(0, 5);       // "hello"
sv.remove_prefix(6);   // sv 变为 "world"
sv.remove_suffix(3);   // sv 变为 "wo"

// 查找(O(n))
sv.find("wo");         // 返回位置
sv.starts_with("he");  // C++20
sv.ends_with("ld");    // C++20

// 转换回 string(这时才分配)
string s(sv);

函数参数 — 首选 string_view

// ❌ 老写法 — 只能接受 std::string
void process(const string &s);

// ❌ 或者重载两个
void process(const string &s);
void process(const char *s);

// ✅ 一个解决所有
void process(string_view sv) {
    // 接受:string, const char*, char[], string_view
}

process("literal");      // ✅ 不构造临时 string
process(s);              // ✅
process(sv);             // ✅

悬空陷阱

// ⚠️ string_view 不拥有数据 → 必须保证数据比 view 活得久

string_view danger() {
    string s = "temp";
    return string_view(s);      // ❌ s 析构后 view 悬空
}

string_view safe() {
    return "literal";           // ✅ 字符串字面量静态存储,永远有效
}

// ⚠️ 临时 string 的陷阱
string_view sv = string("temp") + "file";  // ❌ 临时 string 已析构
string s = string("temp") + "file";
string_view sv2 = s;                        // ✅ s 还在

span — 缓冲区的 string_view

#include <span>  // C++20

// 不拥有数据,只是"看"一段连续内存
void process(span<int> data) {
    for (int &x : data) x *= 2;
}

int arr[] = {1, 2, 3, 4, 5};
vector<int> v = {6, 7, 8};

process(arr);   // ✅ 接受 C 数组
process(v);     // ✅ 接受 vector
// 一个接口覆盖所有连续容器

子区间

vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
span<int> sp = v;

auto first4  = sp.first(4);      // {1, 2, 3, 4}
auto last3   = sp.last(3);       // {6, 7, 8}
auto middle  = sp.subspan(2, 3); // {3, 4, 5}

字节视图

void writeFile(span<const byte> data) {
    // 接受任何连续内存 → 写入文件
}

struct Header { int id; double timestamp; };
Header hdr{1, 3.14};

// 结构体 → 字节视图
writeFile(as_bytes(span{&hdr, 1}));

// 修改底层数据(不能是 const span)
void zeroInit(span<byte> data) {
    fill(data.begin(), data.end(), byte{0});
}

对比总结

stringstring_viewvector<T>span<T>
拥有数据
堆分配
可修改✅ (非 const)
适用场景需存储/修改传参/读取需动态扩容传参/子区间
版本C++98C++17C++98C++20

最佳实践

// 函数参数选择决策树:
//  只读字符串参数     → string_view
//  只读连续内存       → span<const T>
//  需要修改的连续内存  → span<T>
//  需要持有的字符串    → string
//  需要动态增长的容器  → vector<T>

// ❌ 不必要的拷贝
void print(const string &s);
void print(const vector<int> &v);

// ✅ 统一的零拷贝接口
void print(string_view s);
void print(span<const int> v);