类可以坚不可摧(没有析构函数)吗?

Cas*_*eri 18 c++ language-lawyer c++20

考虑:

#include <type_traits>

template <typename T>
struct A {
  ~A() requires(std::is_void_v<T>);
  //~A() requires(!std::is_void_v<T>) = default;
};

template struct A<int>;
Run Code Online (Sandbox Code Playgroud)

我认为一个类不可能没有析构函数,gcc 13.2 似乎同意我的观点,但 clang 17.0 和 msvc 19.38 不同意并抱怨:

[clang] error: no viable destructor found for class 'A<int>'
[msvc ] error C7653: 'A<int>': failed to select a destructor for the class
Run Code Online (Sandbox Code Playgroud)

(将注释行添加到代码中可以解决此问题并使所有编译器满意。)

哪些编译器是正确的?标准中的相关引用是什么?

use*_*522 16

(已完成的)类必须始终具有析构函数。然而,该析构函数可能被隐式声明、删除或未定义。

在类定义的末尾,通过重载决议从预期的析构函数中选择一个析构函数。如果用户没有声明任何预期的析构函数,则隐式声明一个析构函数。因此,对于该重载解决方案,始终至少有一个候选者。如果重载决策失败,则程序格式错误。

所以GCC是错误的,MSVC和Clang是正确的。在您的情况下,没有隐式声明的预期析构函数,因为您手动声明了预期析构函数。该单个预期析构函数在重载解析中不可行,因为不满足其约束。所以重载解析失败。

这在(草案 N4868)[class.dtor]/4中指定。

如果选择的析构函数被定义为删除,那么该类仍然有析构函数,但将无法销毁该类类型的对象,即该类也是“不可破坏的”。任何试图破坏此类对象的程序都是错误的。

类似地,如果选定的析构函数未定义,则任何破坏该类类型的对象的尝试都会使程序因违反单定义规则而导致 IFNDR(格式错误,无需诊断)。