析构函数必须仅可用 (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.
A destructor for a class is odr-used if it is potentially invoked.
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 接受代码,它似乎是一个错误。
请考虑一个结构 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.
A destructor for a class is odr-used if it is potentially invoked.
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 接受代码,它似乎是一个错误。