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

持有std::unique_ptr至不完全类型的类构造函数定义差异原因

问题:默认构造函数定义位置导致unique_ptr编译差异

以下代码可正常编译:

#include <memory>

struct B;

struct A1 {
    A1() = default;
    ~A1();
    std::unique_ptr<B> ptr;
};

#if 0
struct A2 {
    A2();
    ~A2();
    std::unique_ptr<B> ptr;
};
A2::A2() = default;
#endif

int main()
{
}

但将#if 0替换为#if 1编译类A2时,GCC(MSVC下结果类似,C17和C20编译结果一致)会报如下错误:

In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/memory:76,
                 from <source>:1:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:396:17:   required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]'
<source>:17:1:   required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:93:23: error: invalid application of 'sizeof' to incomplete type 'B'
   93 |         static_assert(sizeof(_Tp)>0,
      |                       ^~~~~~~~~~~
ASM generation compiler returned: 1
In file included from /opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/memory:76,
                 from <source>:1:
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h: In instantiation of 'void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]':
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:396:17:   required from 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]'
<source>:17:1:   required from here
/opt/compiler-explorer/gcc-12.2.0/include/c++/12.2.0/bits/unique_ptr.h:93:23: error: invalid application of 'sizeof' to incomplete type 'B'
   93 |         static_assert(sizeof(_Tp)>0,
      |                       ^~~~~~~~~~~
Execution build compiler returned: 1

疑问:A1和A2仅构造函数定义位置不同(均为默认构造),为何编译结果存在差异?


原因解析

核心在于默认构造函数的定义时机,以及unique_ptr对类型完整性的要求:

  • A1的情况:
    A1()是类内默认定义的构造函数。根据C++标准,类内默认定义的特殊成员函数(如默认构造、析构等),其实例化时机被延迟到第一次被调用,或编译器需要生成其代码时。当前代码中main函数并未创建A1的实例,因此A1的默认构造函数和析构函数都不会被实例化。而unique_ptr<B>的析构函数(及其内部default_delete<B>::operator())只有在A1的析构函数被实例化时,才会触发对B类型完整性的检查——此时因未实例化,不会触发sizeof(B)的检查,编译通过。

  • A2的情况:
    A2()是类外默认定义的构造函数(A2::A2() = default;)。对于类外默认定义的特殊成员函数,编译器会在定义点(即=default;所在行)就确定是否需要生成代码。默认构造函数需要初始化成员ptr(std::unique_ptr<B>),更关键的是:处理类外默认定义的构造函数时,编译器会同步检查该类的析构函数是否需隐式生成,进而触发unique_ptr<B>的析构相关代码的实例化检查——此时B仍是不完整类型,default_delete<B>中的static_assert(sizeof(_Tp)>0)会触发错误,因为sizeof不能作用于不完整类型。

简言之:类内默认构造的实例化被延迟,类外默认构造会在定义点触发成员析构代码的检查,而unique_ptr的析构要求指向的类型必须完整。


内容的提问来源于stack exchange,提问作者wohlstad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 08:35:41