Further refinements to the attempt to create a type-dependent expression that is always false
A little while ago, I discussed creating a type-dependent expression that is always false, and I settled upon this:
This covers the cases where
Op is an incomplete type or
void. Billy O’Neal pointed out to me that there are a few cases where this doesn’t work.
One case is if
Op is a reference type. You are not allowed to create pointers to reference types, so the attempt to generate a
false expression will fail with
// MSVC error C2528: 'abstract declarator': pointer to reference is illegal // gcc error: forming pointer to reference type // clang error: 'type name' declared as pointer to a reference
This can be repaired by wrapping
Op in a
static_assert(!sizeof(std::decay_t<Op>*), "Don't know what you are asking me to do.");
But the next part is worse: The
Op could be an abominable function.
I was previously not aware of abominable functions, but upon reading up on them, I’ve concluded that they are fully deserving of their name. It’s like hot lava, fatal poison, and supernatural malfeasance all rolled up into one.
Read up on abominable function types and see if you agree.
The only way to win the game with abominable functions is not to play, so let’s just hand it off to
std::void_t to be neutralized into a
void. This also solves the problem with references, since
std::void_t simply sucks up everything it is given and spits out a
That leaves us with this:
static_assert(!sizeof(std::void_t<Op>*), "Don't know what you are asking me to do.");
At this point, since we know that the only thing that can come out of
void itself, we can tweak the expression to make a false statement a bit more directly:
static_assert(std::is_same_v<std::void_t<Op>, int>, "Don't know what you are asking me to do.");
I’m actually annoyed that the uglyness of abominable functions exists. I think it’s more than the hijinks of the type system, but rather an actual case the compiler just can’t comprehend.
Imagine a read-only memory-mapped IO port. The natural declaration is:
extern uint16_t const volatile port42;
But it doesn’t compile because the compiler authors won’t admit const volatile, until their hand got forced by a const function inside a (pointer to a) volatile struct. ☹
Incidentally I discovered the restriction while trying to use it. The C header file contained a declaration that looked about like that, and it was exported from an assembly module that updated the memory reference that would update it from another thread. The compiler upchucked so I had to take the const off.
If you had a const variable that gets updated elsewhere, it’s not const. If it’s const, the compiler is perfectly within its rights to inline or elide any references, resulting in the changes not getting noticed.
You’re mixing that up with C#. The compiler is not allowed to inline references to extern const like that. C++’s const is more C#’s readonly.
Oh? Can you point out where in the standard it says that? Because I’ve seen compilers do just that. And if not, then what is volatile for?
If you write const int value = something; then yes the compiler can indeed fold something. If you write extern const int value; it cannot. But the compiler in general is allowed to suppress redundant relaods. volatile has the singular purpose of telling the compiler to not do that.
The last line should not have an exclamation point. It should be this:
“Don’t know what you are asking me to do.”);
I was getting very concerned that C++ wasn’t accreting enough complexity. This column has put my mind at rest.