使用std::unique_ptr的Pimpl设计:析构函数前置为何可编译?
Pimpl模式中std::unique_ptr析构函数的编译疑问
代码示例
someclass.h
#include <memory> class SomeClass { public: void do_some_thing(); ~SomeClass(); SomeClass(); private: class SomeClassImp; std::unique_ptr<SomeClassImp> ptr; };
someclass.cpp
#include "someclass.h" #include <iostream> // 为何此处析构函数可编译?此时SomeClassImp并非完整类型 // 若在析构函数内用sizeof(SomeClassImp)会报错,要求完整类型 // 这为何符合std::unique_ptr对完整类型的要求? SomeClass::~SomeClass(){}; SomeClass::SomeClass(){}; class SomeClass::SomeClassImp { public: void implementation() { std::cout << "implementing...\n"; } }; void SomeClass::do_some_thing() { ptr->implementation(); }
问题核心
按照Pimpl模式搭配std::unique_ptr的常规认知,客户端类的析构函数不能内联,必须在实现文件中定义,因为std::unique_ptr的析构函数要求指向的类型是完整类型。但上述代码中,SomeClass的析构函数定义在SomeClassImp完整定义之前却能正常编译,这和直接在析构函数里用sizeof(SomeClassImp)会报错的情况形成矛盾,这是为什么?
原因解析
这本质是C++模板的两阶段名称查找+延迟实例化规则导致的:
std::unique_ptr是模板类,它的析构函数属于依赖模板参数的函数(依赖SomeClassImp这个类型参数)。- 对于模板中依赖参数的函数,编译器不会在首次遇到函数定义时就实例化它,而是会延迟到整个翻译单元的所有定义都可见后才进行实例化。
- 在这个例子里,
SomeClass::~SomeClass()的定义虽然写在SomeClassImp完整定义之前,但std::unique_ptr<SomeClassImp>的析构函数要等到翻译单元末尾(此时SomeClassImp已经被完整定义)才会被实例化,满足了std::unique_ptr析构函数对完整类型的要求。 - 而如果在
SomeClass::~SomeClass()里直接写sizeof(SomeClassImp),这属于非依赖于模板参数的代码,编译器会在解析析构函数定义时就进行检查,此时SomeClassImp还是不完全类型,自然会报错。
内容的提问来源于stack exchange,提问作者shan
相关产品推荐
相关产品推荐

