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

woh*_*tad 6 c++ constructor language-lawyer incomplete-type

下面的代码编译正常(参见下面的 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()
{
}
Run Code Online (Sandbox Code Playgroud)

但如果我用 替换#if 0#if 1编译类,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
Run Code Online (Sandbox Code Playgroud)

神箭

我在 MSVC 上得到了类似的结果。
无论我编译为 C++17 还是 C++20,我都会得到这个结果。

我的问题:
之间的唯一区别是类定义内部或外部的构造函数的定义(在这两种情况下它都定义为)。 为何本案有差异?A1A2default

这个问题是这篇文章的后续问题:为什么 unique_ptr 需要构造函数中的完整类型?

Bri*_*ian 2

区别如下:在 中A1,默认构造函数在其第一个声明中被默认,编译器在需要其定义之前实际上不会定义它,并且由于您在任何时候都不会真正尝试创建对象A1,因此默认构造函数的定义永远不会在此翻译单元中需要,因此编译器永远不会生成定义。至于A2,超出范围的定义

A2::A2() = default;
Run Code Online (Sandbox Code Playgroud)

实际上为它出现的函数生成一个定义。到那时,B一定是完整的,但事实并非如此。这在链接的问题中进行了解释。

请参阅[dcl.fct.def.default]/5

[...] 如果函数是用户声明的并且在第一次声明时未显式默认或删除,则该函数是用户提供的。用户提供的显式默认函数(即,在第一次声明后显式默认)在显式默认时隐式定义;如果这样的函数被隐式定义为已删除,则该程序是错误的。未定义为已删除的非用户提供的默认函数(即在类中隐式声明或显式默认)在使用 odr ([basic.def.odr]) 或需要常量求值 ([表达式.const])。