Further refinements to the attempt to create a type-dependent expression that is always false

Raymond Chen

Raymond

A little while ago, I discussed creating a type-dependent expression that is always false, and I settled upon this:

    static_assert(!sizeof(Op*), "message");

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 std::decay_t:

 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 void.

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 std::void_t is 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.");

 

7 comments

Comments are closed. Login to edit/delete your existing comments

  • Avatar
    Joshua Hudson

    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.

    • Avatar
      Eric Brown

      Joshua –
      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.

          • Avatar
            Joshua Hudson

            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.

  • Avatar
    Phil Deets

    The last line should not have an exclamation point. It should be this:

    static_assert(std::is_same_v<std::void_t<Op>, int>,
    “Don’t know what you are asking me to do.”);