C++ Pimpl模式报Test::Impl不完整类型编译错误排查
问题根因
这个问题和CMake配置无关,是使用std::unique_ptr实现Pimpl模式时的代码编写规则错误。
报错核心原因:
- 你在头文件
test.h中仅前置声明了class Impl;,对所有包含test.h的源文件(比如你的main.cc)来说,Test::Impl是不完整类型——编译器只知道有这么个类,不知道它的大小、成员、析构函数逻辑。 - 你没有为
Test类显式定义析构函数,编译器会自动生成一个内联的默认析构函数,这个析构函数会在调用处(也就是main.cc编译阶段)触发std::unique_ptr<Impl>的析构逻辑。 std::unique_ptr的默认删除器在释放指针时,需要调用Impl的析构函数、通过sizeof(Impl)校验类型完整性,但main.cc里根本看不到Test::Impl的完整定义(Impl的实现写在test.cc里,属于test.cc编译单元的私有内容,不会暴露给其他编译单元),所以触发了invalid application of 'sizeof' to incomplete type 'Test::Impl'的编译错误。
你看到其他项目的Pimpl写法能正常编译,都是因为遵守了「不完整类型搭配unique_ptr时,析构函数必须在类型完整的作用域内定义」的规则,只是你写测试样例时漏掉了这一步。
修复方法
最通用、最符合Pimpl实践的修复方式是显式声明类的析构函数,将析构函数的实现放到源文件中、且位于Impl类完整定义之后:
- 修改
test.h,显式声明析构函数,禁止编译器在头文件内生成内联默认析构:
#pragma once #include "test_interface.h" #include <memory> class Test : public ITest { public: Test(); ~Test(); // 仅声明,不在头文件内实现 private: class Impl; std::unique_ptr<Impl> impl_; };
- 修改
test.cc,在Test::Impl类的完整定义之后,实现析构函数:
#include "test.h" class Test::Impl { public: Impl() {} ~Impl() {} }; Test::Test() : impl_{std::make_unique<Impl>()} {} // 此处编译器已经看到Impl的完整定义,可以安全生成unique_ptr析构逻辑 Test::~Test() = default;
补充注意:如果后续你为Test类自定义了移动构造、移动赋值运算符,也需要遵循同样规则——头文件声明,在test.cc中Impl定义完成后再用
=default实现,否则会触发完全相同的不完整类型编译错误。
内容的提问来源于stack exchange,提问作者Vineet Dwivedi
相关产品推荐
相关产品推荐

