std::out_ptr_t 是 c++23 引入的可调用对象模板别名,用于安全地将智能指针(如 std::unique_ptr)与接受 t 的 c api 配合使用,它通过 operator t() 提供可写入地址,并在析构时自动调用 reset 接管新内存。

std::out_ptr 是什么,它真能接 C API 的裸指针吗?
不能。标准库中根本不存在 std::out_ptr —— 这是常见误解的源头。C++23 引入的是 std::out_ptr_t(带下划线和_t后缀),它是一个**可调用对象模板别名**,用于配合智能指针(如 std::unique_ptr、std::shared_ptr)向 C 风格函数“借出”原始指针并自动更新所有权,但它的行为和使用方式与直觉中的“智能指针替代品”完全不同。
为什么直接传 std::unique_ptr::get() 给 C API 会出问题?
因为多数 C API 的 out 参数要求你传一个 T**(即指针的地址),让函数内部写入新分配的内存地址,例如:
extern "C" int create_buffer(uint8_t** out_buf, size_t* out_len);
如果你只传 ptr.get(),函数写入的是临时值,ptr 本身不会更新,导致资源泄漏或悬空指针。正确做法是让智能指针能「接收」新地址并接管生命周期。这时需要 std::out_ptr_t 或手动处理。
- 错误写法:
create_buffer(&my_ptr.get(), &len)——get()返回右值,取地址非法 - 危险写法:
create_buffer(&raw_ptr, &len); my_ptr.reset(raw_ptr)—— 中间有异常则泄漏 - 推荐路径:用
std::out_ptr_t封装std::unique_ptr,再传给 C 函数
如何用 std::out_ptr_t 安全调用 C 的 out 参数函数?
std::out_ptr_t 不是类型,而是一个函数对象模板别名(定义在 <memory></memory>),必须配合智能指针实例构造。它重载了 operator T**(),返回一个能被 C 函数写入的地址,并在析构时把写入值转交给智能指针管理。
示例(C++23):
std::unique_ptr<uint8_t> buf;
size_t len = 0;
auto op = std::out_ptr_t(buf); // 注意:不是 std::out_ptr
if (create_buffer(op.get(), &len) == 0) {
// buf 已自动接管内存,无需 reset 或 release
use(buf.get(), len);
}</uint8_t>
-
op.get()返回uint8_t**,可安全传给 C 函数 - 若 C 函数成功写入新地址,
op析构时调用buf.reset(新地址) - 若 C 函数失败未写入,
buf保持原状态(包括为空) - 不支持
std::shared_ptr直接用 —— 因为shared_ptr::reset()无法接管已存在的 raw pointer 而不增加引用计数;需改用std::shared_ptr::reset()+ 自定义 deleter 或换回unique_ptr
没有 C++23 怎么办?手写等效逻辑要注意什么?
可以模仿 std::out_ptr_t 行为写一个轻量 wrapper,但必须确保异常安全:
template<typename d>
class c_out_ptr {
std::unique_ptr<uint8_t d>& ptr_;
uint8_t** tmp_;
public:
explicit c_out_ptr(std::unique_ptr<uint8_t d>& p) : ptr_(p), tmp_(nullptr) {}
~c_out_ptr() { if (tmp_) ptr_.reset(*tmp_); }
operator uint8_t**() { return tmp_ = &ptr_.replace(nullptr); }
};</uint8_t></uint8_t></typename>
注意点:
-
ptr_.replace(nullptr)是 C++23 新增成员,旧标准需用std::exchange(ptr_, nullptr).release()模拟 - 必须用
std::exchange或类似机制,否则ptr_.get()在赋值前仍持有旧指针,reset()会提前释放 - 所有中间步骤(包括
operator T**调用)都不能抛异常,否则析构可能不执行,导致泄漏 - Windows 的
CoTaskMemAlloc等 COM API 需配对CoTaskMemFree,此时自定义 deleter 必须准确匹配
真正麻烦的从来不是语法,而是 C API 对内存归属的隐式约定——比如有些函数要求调用方 free,有些要求它自己 free,还有些文档里没写清楚。用 std::out_ptr_t 只解决“怎么交出去又拿回来”,不解决“该不该交、交完谁负责”。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











