In the following code, the last two static_asserts fail.
But why? Where should I put the const to make the assertations pass?
#include <type_traits>
// these are ok
static_assert(!std::is_same_v<void(), const void()>);
static_assert(!std::is_same_v<void(), void() const>);
static_assert(!std::is_same_v<const void(), void() const>);
// so 'void()', 'const void()', 'void() const' are all distinct types
using F = void(); // or 'typedef void F()';
static_assert(std::is_same_v<const F, const void()>); // error!
static_assert(std::is_same_v<const F, void() const>); // error!
The above code may seem too abstract and like a pure language-lawyer code, so I'll explain in detail what led me to the question.
Real-life example
I had the following code:
#include <type_traits>
template <class>
struct X;
template <class T, class U>
struct X<T U::*> {
using t = T;
};
struct A {
void member() {}
};
static_assert(std::is_same_v<
X<decltype(&A::member)::t, void()
>); // this is ok
My intention was that the X<pointer-to-member-function>::t would be the type of the written-outside-the-class version of the member-function (You probably know what I mean). And the code above runs fine, that is, the static_assert passes.
Next, I changed the definition of A to the following:
struct A {
void const_member() const {}
};
and the static_assert to:
static_assert(std::is_same_v<
X<decltype(&A::const_member)::t, void()
>);
Now the static_assert fails.
But then I noticed that if I changed the void() in the static_assert to void() const it compiles. So I changed the partial speciailization of X to the following (so that X::t would be void())
template <class T, class U>
struct X<const T U::*> {
using t = T;
};
but then, the compiler complains:
error: incomplete type ‘X<void (A::*)() const>’ used in nested name specifier
17 | X<decltype(&A::const_member)>::t, void()
Apparently, the specialization is not selected.