std::make_unique不能直接传入裸指针,因其仅接受构造参数用于创建新对象并返回unique_ptr,不支持接管已有指针;正确方式是用std::unique_ptr(p)显式构造。

std::make_unique 为什么不能直接传入裸指针
它根本不是用来包装已有指针的——std::make_unique 的唯一作用是**构造新对象并返回其 std::unique_ptr**。试图用它“接管”一个已存在的裸指针(比如 new 出来又想转交)会编译失败,因为它的参数列表只接受构造参数,不接受 T*。
常见错误现象:std::make_unique<int>(p)</int> 看似想包装指针 p,实际创建的是一个指向 int* 类型的新对象(即 int**),完全偏离意图。
- 正确做法:若已有裸指针需托管,应显式构造
std::unique_ptr,如std::unique_ptr<int>(p)</int> -
std::make_unique的签名是template<class t class... args> unique_ptr<t> make_unique(Args&&... args)</t></class>,所有参数都用于调用T的构造函数 - 它避免了
new和unique_ptr构造之间的异常安全漏洞(中间若抛异常,new的内存会泄漏)
如何传递参数给被构造的对象
参数按值或引用转发给目标类型的构造函数,支持完美转发。关键在于区分“构造对象所需参数”和“智能指针自身配置”——std::make_unique 不接受自定义删除器、不支持数组特化(std::make_unique<t></t> 是合法语法,但不支持带尺寸的初始化,且不能传构造参数)。
- 普通类型:
auto p = std::make_unique<:string>("hello")</:string>→ 调用std::string(const char*) - 带多个参数:
auto p = std::make_unique<:vector>>(10, 42)</:vector>→ 构造含 10 个 42 的 vector - 自定义类:
auto p = std::make_unique<myclass>(arg1, arg2)</myclass>,要求MyClass有对应构造函数 - 数组形式仅支持默认构造:
auto arr = std::make_unique<int>(100)</int>,但std::make_unique<int>(100, 42)</int>是非法的
与 new + unique_ptr 构造的性能和安全性差异
表面上只是写法不同,但底层行为影响异常安全和可读性。使用 new 显式分配再传给 unique_ptr 构造函数时,若构造函数本身抛异常(例如 unique_ptr 的自定义删除器构造失败),new 的内存无法自动释放。
立即学习“C++免费学习笔记(深入)”;
-
std::make_unique是原子操作:要么对象成功构造且指针返回,要么全程回滚,无资源泄漏风险 - 编译器对
make_unique的优化更充分,通常不产生额外开销;而new+ 构造可能因临时对象引入冗余移动 - 不能用
std::make_unique替代带自定义删除器的场景,例如文件句柄管理必须写成std::unique_ptr<file decltype>(fopen(...), &fclose)</file>
常见编译错误及修复
最典型的报错是 “no matching function for call to ‘make_unique’”,往往源于参数类型不匹配或误用了数组语法。
-
error: no matching function for call to ‘make_unique<int>(int, int)’</int>→ 数组形式不接受初始化参数,改用std::make_unique<int>(n)</int>后手动赋值,或改用std::vector -
error: use of deleted function ‘std::unique_ptr<...>::unique_ptr(const std::unique_ptr<...>&)’</...></...>→ 把make_unique结果赋给了非右值引用(如写了auto p = std::make_unique<t>(); auto q = p;</t>),应改为auto q = std::move(p) - 模板推导失败(如传入
nullptr)→ 显式指定类型:std::make_unique<int>(0)</int>而非std::make_unique<int>(nullptr)</int>
真正容易被忽略的是:即使你只用单参数构造,也要确认该类型支持移动语义(尤其自定义类),否则 make_unique 返回的临时 unique_ptr 在赋值时可能触发编译错误。


















