C++ timebomb types aka Foot Gun with a Timer

IT realy hit hard, this time

Do not use static_assert to constrain the types you are developing. Huh?  What does that even mean? Why not? Is that even an issue?

[Update: Added dbj_constraint]

Currently using std:: well-known containers, to the surprise of many, one can create all sorts of timebomb types, and then someone else, sometimes in the future, unknowingly and innocently might code all sorts of well-hidden timebombs, using those types.

We call them “timebomb types”, because they might be unnoticed “under the radar”, for months or even years and just then make your code not compile, aka “bomb”. Godbolt is here.

The key problem

Technically template is not a type. The template is a type waiting to happen. Template definition is a type.

static_assert does not constrain the creation of type, it does constrain the instantiation of a type.

Consider this example from one young c++ author.

timebomb_type is a derived type. timebomb type can be defined as a new type and compiled as such, but no instances of that type can be ever made. Any attempt to do so will be stopped by the compiler.

Exactly the same timebomb “constraint”, using static_assert is deployed inside (for example) MS STL.

To add the oil to that grill, the key issue is that timebomb_type can be used to create other types further down the line. That is because static_assert (if used that is, to check the type constraints) will “kick in” just when someone tries to instantiate the type. Not before. Few examples, with important info on the location of the offending code:

Above is what one might name “C++ sitcom”. Comedy of errors no one (in the comedy) is aware of.  And, there is more. Template instantiation anyone? We have that too.

Every compiler will obey and compile the above without a single grudge. Imagination is the only constraint (pun intended) here. All sorts of well-hidden timebombs and foot guns can be produced in standard C++ with no or very little warning from the compiler.

And no warning to the future unfortunate users carrying around third-party code with timebomb types. Try it yourself in the Godbolt.

The simple remedy

Just a type-constrained template is enough to stop wrong type definitions down the line. And you constrain the type with std::enable_if; in standard C++17.

The outcome is, the user (or you) can not introduce types whose usage will not compile only a few months or years down the line.

This post describes a  peculiarity one might describe as “C++ foot gun with a timer” …

C++20

Using C++20 and beyond, one can boldly use the “dreaded” constraints, everybody is afraid of. Here is one example  to try and convince you to look into the C++20  requires keyword:

Not that complicated. It looks quite clear actually.  If you try and create a “wrong” type from that template you will get much clearer error messages vs using the good old std::enable_if. Try.

But wait, there is even more!

It was brought to my attention, there are unfortunate souls not allowed to enjoy  C++17, 14, and even C++11.  Here is the solution.

With simple ad-hoc macros, we create the ad-hoc “requirements”. Any compile-time expression that resolves to true or false will do.

Let us use the above to create a constrained type for pre C++11 versions.

Macro aficionados can make that code more “clever” or “convoluted”, it depends on you letting them in your space. And now the usage:

For me, that might be even better vs std::enable_if. It certainly seems simpler. And here is the mandatory Godbolt,