如何编写自定义删除器处理前向声明类型(VS2012兼容方案)
关于VS中unique_ptr删除器与不完整类型的问题解答
一、哪个版本修复了default_deleter需要完整类型的问题?
VS2015及后续版本修复了这个不符合C++标准的行为。
按照C++11及以后的标准,std::default_delete<T>只在**实际执行删除操作(比如unique_ptr析构、调用reset方法)**时,才要求T的完整定义;而旧版VS(如2012)错误地在声明unique_ptr<T>的阶段就强制要求T必须是完整类型,这个问题在VS2015中被修正,对齐了标准规范。
二、VS2012中使用仿函数删除器的兼容方案
你遇到的C4150警告,本质是VS2012在处理非模板类的模板成员函数时,提前实例化了MyDeleter::operator()<Foo>,此时Foo还是不完整类型,delete不完整类型的指针会触发警告(甚至可能导致析构函数未被调用)。下面提供几个可行的兼容方案:
方案1:将仿函数改为模板类
把MyDeleter改成针对T的模板类,让删除器的实例化推迟到Foo完整定义的时候:
// 前向声明Foo class Foo; template <typename T> struct MyDeleter { void operator()(T* param) { delete param; } }; // 声明智能指针 std::unique_ptr<Foo, MyDeleter<Foo>> pFoo; // 在Foo完整定义的位置赋值 pFoo = decltype(pFoo)(new Foo);
这样MyDeleter<Foo>::operator()的实例化会延迟到赋值语句执行时,此时Foo已经是完整类型,VS2012就不会触发C4150警告了。
方案2:延迟模板成员函数的实现
如果不想修改仿函数的结构,可以把MyDeleter::operator()的实现放在Foo完整定义之后,比如通过模板特化的方式:
// 前向声明Foo class Foo; struct MyDeleter { // 只声明模板成员函数 template <typename T> void operator()(T* param); }; // 这里是Foo的完整定义 class Foo { // Foo的成员定义 }; // 特化MyDeleter的operator()<Foo>,此时Foo已经完整 template <> void MyDeleter::operator()<Foo>(Foo* param) { delete param; } // 声明并赋值智能指针 std::unique_ptr<Foo, MyDeleter> pFoo = decltype(pFoo)(new Foo);
这种方式确保编译器实例化删除器的具体实现时,Foo已经是完整类型,避免了警告。
方案3:临时禁用C4150警告(不推荐)
如果以上方案都无法适配你的代码结构,可以临时禁用该警告,但要注意这可能隐藏真正的问题(比如误删不完整类型导致资源泄漏):
#pragma warning(disable: 4150) // 声明并赋值智能指针的代码段 std::unique_ptr<Foo, MyDeleter> pFoo = decltype(pFoo)(new Foo); #pragma warning(default: 4150)
内容的提问来源于stack exchange,提问作者Jonathan Mee
相关产品推荐
相关产品推荐

