下面的代码编译正常(参见下面的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,都会得到这个结果。
我的问题:
A1
和A2
之间的唯一区别是类定义内部或外部的构造函数的定义(在这两种情况下,它都被定义为default
)。
为什么在这种情况下会有不同?
- 此问题是此帖子的后续:为什么unique_ptr在构造函数中需要完整的类型?*
1条答案
按热度按时间bogh5gae1#
区别在于:在
A1
中,默认构造函数在第一次声明时默认,编译器直到需要它的定义时才真正定义它,并且由于在任何时候你都不会真正尝试创建A1
对象,所以在这个翻译单元中永远不需要默认构造函数的定义,所以编译器永远不会生成定义。实际上在函数出现的地方生成了函数的定义。此时,
B
必须是完整的,但它不是。这在链接的问题中解释。参见[dcl.fct. def.默认值]/5:
[...]如果一个函数是用户声明的,并且在其第一次声明时没有显式默认或删除,则该函数是 * 用户提供的 *。用户提供的显式默认函数(即,在其第一次声明之后显式默认)在其显式默认的点处被隐式定义;如果这样的函数被隐式定义为已删除,则程序是病态的。一个非用户提供的默认函数(即类中隐式声明或显式默认的),如果未被定义为已删除,则在odr使用([basic.def.odr])或常量求值需要([expr.const])时被隐式定义。