When explicitly defaulting a destructor (e.g.: struct s { ~s() = default; };), it seems that the type can still be used in a constexpr context. However, when explicitly deleting a destructor (e.g.: struct s { ~s() = delete; };), Clang no longer thinks the type is viable for a constexpr context. So, as the title suggests, can a type with an explicitly deleted destructor still be used in a constexpr context?
struct a { ~a() = default; }; // all ok
struct b { ~b(){} }; // all fail
struct c { ~c() = delete; }; // fails on clang
struct d { constexpr ~d() = default; }; // all ok
struct e { constexpr ~e(){} }; // all ok
struct f { constexpr ~f() = delete; }; // all ok
static_assert(noexcept([]<c>{}));
The error produced by Clang 16.0.0 and Clang trunk:
<source>:9:28: error: non-type template parameter has non-literal type 'c'
static_assert(noexcept([]<c>{}));
^
<source>:3:12: note: 'c' is not literal because its destructor is not constexpr
struct c { ~c() = delete; }; // fails on clang
^
Live example
For a class to be a literal type, all you need is a constexpr destructor. = deleted functions have been allowed to be constexpr for a long time.
However, if you don't mark it constexpr, then it isn't constexpr except defaulted destructors which have an extra rule ([class.dtor]p9):
A defaulted destructor is a constexpr destructor if it satisfies the requirements for a constexpr destructor ([dcl.constexpr]).
Which a class with no members does, so ~a() = default; is constexpr.
~c() = delete; isn't constexpr because there's no reason for it to be.
constexpr ~f() = delete; is constexpr because it's marked constexpr.
Clang is correct here: ~s() = delete; would not be constexpr if it is not specified to be, so can't be used as a literal type. It seems that std::is_literal_v<c> passes in gcc erroneously.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With