第9.13章: string_view 与 cstring_view
概述
::fast_io::string_view 与 ::fast_io::cstring_view 都是对字符序列的非拥有视图。它们很轻(指针 + 大小),不需要复制。关键区别在于 cstring_view 保证以空字符结尾。
字符串视图还有许多其他操作(查找、修剪、比较等)。你可以先跳过这些细节,需要时再回到附录第9.13.1节。
1. basic_string_view
string_view 就是一个指针加一个大小。用它把字符串传给函数,不产生额外开销:
#include <fast_io_dsal/string_view.h>
#include <fast_io_dsal/string.h>
#include <fast_io.h>
void greet(::fast_io::string_view name) {
::fast_io::io::println("Hello, ", name, "!");
}
int main() {
::fast_io::string str{"Alice"};
greet(str); // From string (implicit conversion)
greet("Bob"); // From string literal
// Sub-view operations
::fast_io::string_view sv{"Hello, World!"};
auto prefix = sv.subview_front(5zu); // "Hello"
auto suffix = sv.subview_back(6zu); // "World!"
auto middle = sv.subview(7zu, 5zu); // "World"
// Trim operations
::fast_io::string_view padded{" hello "};
auto trimmed = padded.trim_subview_c_space(); // "hello"
// Search
auto pos = sv.find_character('W'); // 7
bool has = sv.contains_character('o'); // true
}
字符类型变体
| 别名 | 字符类型 |
|---|---|
::fast_io::string_view | char |
::fast_io::wstring_view | wchar_t |
::fast_io::u8string_view | char8_t |
::fast_io::u16string_view | char16_t |
::fast_io::u32string_view | char32_t |
2. cstring_view
::fast_io::cstring_view 是带空字符结尾保证的 string_view。它可以由 string(始终以空字符结尾)和字符串字面量构造。在与 C API 交互时使用它:
#include <fast_io_dsal/string.h>
#include <fast_io_dsal/cstring_view.h>
#include <fast_io.h>
void c_api_wrapper(::fast_io::cstring_view csv) {
// Safe to pass .c_str() to C functions
// ::some_c_function(csv.c_str());
}
int main() {
::fast_io::string str{"Hello"};
::fast_io::cstring_view csv{str}; // OK: string is always null-terminated
c_api_wrapper(csv);
}
3. 悬空的 string_view
string_view 不拥有它的数据。它只是一个指针加一个大小 — 如果底层缓冲区被搬移或释放,这个视图就会悬空。
安全的部分:临时量不会造成悬空
fast_io::string_view 与 fast_io::span 只接受左值引用。这意味着你无法意外地从临时量创建视图 — 编译器会拒绝:
// This does NOT compile — string_view only binds to lvalues
::fast_io::string_view get_name() {
::fast_io::string temp{"hello"};
return temp; // ERROR: cannot create string_view from a local that is about to die
}
因此其他语言里那种经典的悬空模式在这里不可能出现。编译器保护你不受指向已销毁对象的视图之害。
危险的部分:重新分配会使视图失效
string 的 string_view 会在该 string 重新分配时悬空。push_back()、insert_index()、replace_index()、append() 以及 operator+= 等操作都可能触发重新分配,把缓冲区搬到新的内存。已有的 string_view 仍然指向旧的(已释放的)缓冲区。
::fast_io::string s{"Hello, World!"};
::fast_io::string_view sv{s}; // sv points into s's buffer
// DANGER: this may reallocate s's internal buffer
s.append(" this is a long string that forces reallocation");
// sv is now dangling — it points to freed memory
// Reading sv is undefined behaviour
对指向 vector 或 deque 的 span 也是如此:任何使容器增长的操都可能使已有视图失效。
经验法则:不要跨任何可能修改底层容器的操作持有 string_view(或 span)。
要在测试期间发现悬空视图,可以使用 Wasm MemTag(WebAssembly 内存标记)。在启用内存标记的情况下编译到 Wasm 后,对已释放内存的访问会在运行期被捕获,从而使悬空视图的 bug 可被发现。
要点
string_view是只含指针和大小的非拥有视图,传递时不产生额外开销。cstring_view额外保证以空字符结尾,便于与 C API 互操作。- 所有字符类型变体都可用:
string_view、wstring_view、u8string_view等。 string_view的迭代器总是 const(不可变视图)。string_view只绑定左值引用 — 编译器阻止从临时量产生悬空。- 不过,当底层
string重新分配时(通过insert_index、replace_index、append等),string_view仍会悬空。不要跨修改操作持有视图。 - 测试时用 Wasm MemTag 检测悬空视图。
- 完整的操作参考见附录第9.13.1节。