You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:06:10