A. Jiang <de34@live.cn> found that: Quote: "The return type is... cursed for cv void. It seems that decltype(std::declval<const void>) should be const void() (until C++17) / const void() noexcept (since C++17) (same for volatile void and const volatile void), but the cv-qualifiers are dropped" Code example: #include <utility> #include <type_traits> int main() { static_assert(std::is_same_v<decltype(std::declval<const void>), const void () noexcept>); static_assert(std::is_same_v<decltype(std::declval<volatile void>), volatile void () noexcept>); } https://gcc.godbolt.org/z/TMe3v9sxh I've created the same issue for libc++: https://github.com/llvm/llvm-project/issues/61232
Who cares? Taking the address of std::declval is forbidden, and you can't call that specialization anyway.
The reason it behaves this way is that we define declval as: template<typename _Tp> auto declval() noexcept -> decltype(__declval<_Tp>(0)); and decltype(__declval<_Tp>(0)) drops the cv-qualifiers. This implementation is much faster to compile, and so we're not going to stop using it to "fix" the useless testcase in this bug. Nobody cares about decltype(std::declval<const void>) but they do care about compile times.
I've mailed to LWG Chair to request legitimation of libc++ and libstdc++'s current strategy.
I thought it was already allowed, because std::declval is not an addressable function ([namespace.std]) but that only covers forming pointers and references to functions. We should extend that to cover this case.