c++ 将std::unique_ptr保存为不完整类型的类的构造函数定义之间的差异

c3frrgcw  于 2023-02-17  发布在  其他
关注(0)|答案(1)|浏览(109)

下面的代码编译正常(参见下面的Golbolt链接):

#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 1替换#if 0来编译类A2,我会从gcc得到以下错误:

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

Godbolt
我在MSVC上得到了类似的结果。
无论我编译为C17还是C20,都会得到这个结果。

我的问题:

A1A2之间的唯一区别是类定义内部或外部的构造函数的定义(在这两种情况下,它都被定义为default)。
为什么在这种情况下会有不同?

  • 此问题是此帖子的后续:为什么unique_ptr在构造函数中需要完整的类型?*
bogh5gae

bogh5gae1#

区别在于:在A1中,默认构造函数在第一次声明时默认,编译器直到需要它的定义时才真正定义它,并且由于在任何时候你都不会真正尝试创建A1对象,所以在这个翻译单元中永远不需要默认构造函数的定义,所以编译器永远不会生成定义。

A2::A2() = default;

实际上在函数出现的地方生成了函数的定义。此时,B必须是完整的,但它不是。这在链接的问题中解释。
参见[dcl.fct. def.默认值]/5:
[...]如果一个函数是用户声明的,并且在其第一次声明时没有显式默认或删除,则该函数是 * 用户提供的 *。用户提供的显式默认函数(即,在其第一次声明之后显式默认)在其显式默认的点处被隐式定义;如果这样的函数被隐式定义为已删除,则程序是病态的。一个非用户提供的默认函数(即类中隐式声明或显式默认的),如果未被定义为已删除,则在odr使用([basic.def.odr])或常量求值需要([expr.const])时被隐式定义。

相关问题