C++类型安全进阶:span、expected与variant替代裸指针【现代C++范式】

9次阅读

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

C++类型安全进阶:span、expected与variant替代裸指针【现代C++范式】

为什么 std::span 能安全替代 T* + size_t 参数对

裸指针加长度的组合在函数接口中极易引发越界、悬空或生命周期不匹配问题,std::span 把二者绑定为一个不可分割的视图对象,且不拥有数据——既避免拷贝开销,又强制调用方明确传递有效范围。

常见错误现象:process(arr, n)n 传错、arr 已析构、arr 实际长度小于 n;而 process(std::span(arr, n)) 在编译期就能捕获多数尺寸不匹配(如数组字面量推导),运行时构造失败也会触发断言(取决于实现)。

  • std::span 默认要求元素类型可平凡复制(trivially copyable),对非 POD 类型需显式使用 std::span<:byte> 或配合 reinterpret_cast
  • 传参优先用 std::span 表达只读意图,避免意外修改原容器
  • 不要从局部数组取 std::span 并返回——生命周期仍由原数组决定,span 不延长生存期

std::expectedstd::optional 或异常更适合哪些错误场景

std::expected 明确区分「成功值」和「可预期的错误原因」,适用于错误可分类、需透传上下文、且不希望用异常打断控制流的场合(如解析配置、系统调用封装异步 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> —— 这反而引入间接和分配,失去 variant 的优势。

  • 成员类型必须互不相同;若需多个同类型(如多个 int),改用带 tag 的结构体包装
  • 访问时优先用 std::visit([](const auto& v) { ... }, var),避免重复写 std::holds_alternative 分支
  • std::monostate 可作默认初始化占位符,但注意它不参与相等比较(std::variant{}std::variant{std::monostate{}} 不等)

裸指针还没彻底淘汰?这些边界情况仍需谨慎处理

现代 C++ 并未禁止裸指针,而是限制其使用范围:new/delete 配对、C API 交互、低层内存管理(如自定义 allocator)、以及某些模板元编程技巧中仍会见到。关键在于——裸指针只应出现在你**明确承担全部生命周期责任**的地方。

容易被忽略的点:std::spanstd::string_view 都不保证底层内存对齐;若对接 SIMD 或硬件寄存器映射,仍需用 alignas + 原始指针,并手动校验地址。

  • 与 C 函数交互时,用 std::data(span) 取指针比 &span[0] 更安全(前者对空 span 返回合法空指针,后者未定义)
  • 禁用隐式转换:给接受裸指针的旧接口封装一层,参数强制为 std::spanstd::string_view,内部再转出指针
  • 静态分析工具(如 clang-tidy 的 cppcoreguidelines-pro-bounds-pointer-arithmetic)可帮你定位残留的危险指针算术
text=ZqhQzanResources