<p>std::span 能安全替代 T* + size_t,因其将指针与长度绑定为不可分割的视图,不拥有数据、避免拷贝,且在编译期或运行时捕获尺寸错误与生命周期问题。</p>

为什么 std::span 能安全替代 T* + size_t 参数对
裸指针加长度的组合在函数接口中极易引发越界、悬空或生命周期不匹配问题,std::span 把二者绑定为一个不可分割的视图对象,且不拥有数据——既避免拷贝开销,又强制调用方明确传递有效范围。
常见错误现象:process(arr, n) 中 n 传错、arr 已析构、arr 实际长度小于 n;而 process(std::span<int>(arr, n))</int> 在编译期就能捕获多数尺寸不匹配(如数组字面量推导),运行时构造失败也会触发断言(取决于实现)。
-
std::span默认要求元素类型可平凡复制(trivially copyable),对非 POD 类型需显式使用std::span<:byte></:byte>或配合reinterpret_cast - 传参优先用
std::span<const t></const>表达只读意图,避免意外修改原容器 - 不要从局部数组取
std::span并返回——生命周期仍由原数组决定,span不延长生存期
std::expected 比 std::optional 或异常更适合哪些错误场景
std::expected<t e></t> 明确区分「成功值」和「可预期的错误原因」,适用于错误可分类、需透传上下文、且不希望用异常打断控制流的场合(如解析配置、系统调用封装、异步 I/O 结果)。
对比:std::optional 只能表达“有/无”,无法携带错误信息;抛异常在性能敏感路径或禁用异常的环境(嵌入式、游戏引擎)中不可行。
立即学习“C++免费学习笔记(深入)”;
- 错误类型
E应轻量且可复制,推荐用enum class(如enum class parse_err { invalid_format, out_of_range })而非std::string - 不要把
std::expected当作万能错误容器——若错误不可恢复或属编程缺陷(如空指针解引用),仍应使用断言或异常 - 链式调用时可用
.and_then()替代嵌套if (e.has_value()),但注意 GCC 13 前部分实现未完全支持 C++23 的扩展操作符
何时该用 std::variant 而不是虚函数或多态指针
当类型集合固定、数量有限、且操作集中在单个函数内(如序列化、比较、格式化),std::variant 比虚函数更高效:无虚表查表、无动态内存分配、编译期确定布局;它天然支持访问者模式,也更容易做 constexpr 计算。
典型误用:std::variant<:unique_ptr>, std::shared_ptr<base>></:unique_ptr> —— 这反而引入间接和堆分配,失去 variant 的优势。
- 成员类型必须互不相同;若需多个同类型(如多个
int),改用带 tag 的结构体包装 - 访问时优先用
std::visit([](const auto& v) { ... }, var),避免重复写std::holds_alternative分支 -
std::monostate可作默认初始化占位符,但注意它不参与相等比较(std::variant<int std::monostate>{}</int>和std::variant<int std::monostate>{std::monostate{}}</int>不等)
裸指针还没彻底淘汰?这些边界情况仍需谨慎处理
现代 C++ 并未禁止裸指针,而是限制其使用范围:new/delete 配对、C API 交互、低层内存管理(如自定义 allocator)、以及某些模板元编程技巧中仍会见到。关键在于——裸指针只应出现在你**明确承担全部生命周期责任**的地方。
容易被忽略的点:std::span 和 std::string_view 都不保证底层内存对齐;若对接 SIMD 或硬件寄存器映射,仍需用 alignas + 原始指针,并手动校验地址。









