析构函数必须仅可用 (public) 或对默认初始化的 class 成员完全有效?

Must destructor be just available (public) or fully valid for default initialized class members?

请考虑一个结构 A,它有一个类型为 U<R> 的字段 u,带有默认初始化程序。析构函数~U<R>只被声明为:

template<typename T>
struct U {
    ~U();
};

struct R;

struct A {
    U<R> u = U<R>{};
};

所有编译器都接受此代码,演示:https://gcc.godbolt.org/z/oqMjTovMo

但是如果我们定义析构函数~U<R>如下:

template<typename T>
struct U {
    ~U() { static_assert( sizeof(T) > 0 ); }
};

然后当前的编译器出现分歧。 MSVC 不断接受程序,同时 GCC/Clang 打印错误

error: invalid application of 'sizeof' to an incomplete type 'R'

演示:https://gcc.godbolt.org/z/713TzPd6v

显然,编译器必须验证默认初始化 class 成员的析构函数的可用性,以防在构造期间发生异常。但是标准是否要求编译器只检查析构函数的可用性(如 MSVC 所做的那样),还是编译器也应该验证其主体? MSVC 行为在这里看起来更方便,因为它允许在结构 A 定义时前向声明 R

P。 S. 这种探索不仅具有纯粹的理论兴趣。如果在这里用 std::unique_ptr 替换 U 那么它解释了为什么 class 类型 std::unique_ptr<R> 的字段被 MSVC 接受并被 GCC/Clang 拒绝不完整 classes R.

[dcl.init.aggr]/8:(强调我的)

The destructor for each element of class type is potentially invoked ([class.dtor]) from the context where the aggregate initialization occurs.

[basic.def.odr]/8:

A destructor for a class is odr-used if it is potentially invoked.

[basic.def.odr]/10:

Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program outside of a discarded statement; no diagnostic required.

因此 ~U() 可能在 A 内的 U<R> u 调用,这需要它的定义。

然后根据[temp.inst]/4

Unless a member of a class template or a member template is a declared specialization, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist ...

附注:当然,每当 A 的一个实例被销毁时,它 实际上是 invoked。所以需要编译。

注意:在第一个版本中,编译器接受代码,因为析构函数不是内联的,因此在链接过程中失败(example)。

至于 MSVC 接受代码,它似乎是一个错误。