Do you know this, sometimes you have a class that should only be instantiable at compile-time? By that, you can build several assumptions on such a class. For example, that the data is read-only, not on the local stack, nor dynamic memory.
Using some code to illustrate the situation, if you look at the function signature below, the requirement is that the first parameter
key contains data that persist during the entire run-time of the program.
Let's assume we can achieve that, then that knowledge can lead to efficient designs like this string can be used as a key in a data structure where instead of a copy the string, you can store the pointer to the string since the data will always be valid.
The question is how to achieve this. And know what? The answer is: it depends! Of course! But on what? Well, the C++ standard.
Let's start with the latest version, C++20, before we look at a C++17 solution.
A C++20 and later approach
C++20 comes with the right tool here, the missing piece to the
consteval keyword. While
constexpr implies execution at compile- or run-time,
consteval or immediate functions are only callable during compile-time.
With that knowledge, here is a C++20 implementation:
1 2 3 4 5 6 7 8 9 10 11 12
Here the new feature
consteval is handy. You mark the constructor of the class as
consteval to ensure that this constructor is invokable only at compile time. This gives you an object that must be created at compile time. While the object itself might go out of scope, the data that the object refers to doesn't.
Here is how you use call
We're done. That was fast, right? If you still have time or if you're using C++17, have a look at the next section.
A C++17 approach (or earlier)
As wonderful as
consteval is, what if you're still at C++17?
constexpr alone will not do the trick due to its dual nature someone can invoke a
constexpr constructor at compile- and run-time.
There is a trick you can employ to achieve the same result. Since C++11, we have user-defined literals and the corresponding new operator, the literal operator. The great thing about the literal operator is that it only takes compile-time values as input. Essential, strings or numbers. Both inputs must be known at compile time. That matches our requirement. But what about the constructor?
The change you have to make is moving the constructor into the protected or private scope. Which one is up to you. If you additionally make the literal operator for that class a friend, this operator is the only way to instantiate that class. Voila, this is your solution to a class that has the guarantee to be only instantiable at compile-time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
This time you need the UDL when calling
You can mix this approach with C++17s
string_view, for example, by adding a conversion operator to a
string_view object to the class
I hope this helps you at some point to sharpen your interfaces while still keeping the footprint of your binary low.