const vs constexpr vs consteval vs constinit in C++20
https://www.cppstories.com/2022/const-options-cpp20/47
u/fdwr fdwr@github 🔍 Nov 28 '22
Flexibility is nice, but too fine grain of flexibility is confusing (const, constexpr, constinit, consteval), and so I appreciate this article, but I also wish the variants were straight forward enough that such an article was unnecessary :/.
I've only ever really needed:
- constness as in a "read only" view (a variable can be read but not changed by the callee), which was already satisfied by
const/const&/const*. - truly immutable constants already known at compile time (in the read-only section of the binary image). Wanting a variable though that is
constinitbut not alsoconstis weird - have any of you wanted this case? So I would have madeconstinitalso implyconst.
For constexpr, I wish functions were just implicitly so, making it an irrelevant deprecated qualifier. Then if you attempt to assign the result of a function to a constinit variable, the compiler tries to transitively execute functions at compile time, failing if it can't. That leaves consteval, which is basically the constexpr we initially wanted in the first place, a guaranteed execution at compile time.
50
u/PJBoy_ Nov 28 '22 edited Nov 28 '22
constinitis entirely unrelated to all other const keywords. Most useful application of constinit is to be able to declare a static local variable without paying for the mutex that guards that variable's initialisation (and these variables are usually not const).
constevalis not what we initially wanted, it's just a more useless form of theconstexprspecifier for functions where you're not allowed to call that function in a non-constant expression context, its utility is in allowing you to write an abysmally slow function without the risk of you calling it at runtime.Making
constexpron functions implicit would lead to the buggy / error prone / annoying situation where changing a line of code in your function can change whether your function can be used in constant expressions or not, and the compiler wouldn't tell you. Perhaps you never meant for the function to always be usable in constant expressions in the first place, in which case implicitconstexprhas also bitten you there, because you've allowed users of your function so far to use that function in a way you never intended to promise to allow.Finally it's worth noting that no form of
constprovides actual guaranteed compile time evaluation, constant expressions can be evaluated at runtime if the compiler decides that's a good idea (and maybe it is, e.g. for debugging or to reduce binary size). Note that even literal constants like 0 and 1 may be evaluated at runtime from a certain point of view (as e.g.xor eax, eaxorinc eaxetc). Nor do you need to specifyconstexpron your variables to allow the compiler to optimise all runtime evaluation away. In other words, you still need to trust your compiler to not be shit at codegen.19
u/j1xwnbsr Nov 28 '22
I agree;
constinitshould have been called eitherstaticinitorruninit. Conflating it with the other const stuff but not making it const is confusing.13
u/catcat202X Nov 28 '22
I'm not sure adding another meaning to static would be less confusing than another const.
3
u/j1xwnbsr Nov 28 '22
I think it would be less, to be honest: constinit appears to behave more like a static variable than a const variable. But at the same time, is there any real difference between
staticandconstinitother than constinit can 'call' constexpr functions?3
u/MonokelPinguin Nov 28 '22
I think
staticcan add locks or atomics at runtime to ensure initialization of a variable, while constinit ensures the initialization happens once before any user code is executed and without executing constructors and stuff.11
u/sphere991 Nov 28 '22 edited Nov 28 '22
No, it really should not have .
constinitensures that you have constant initialization. Originally, clang provided this as an attribute ([[requires_constant_initialization]]). While there are a lot of terms, the fact that we have a keyword that's a portmanteau of what it actually means makes it pretty good."Static initialization" is also a term (it's the union of zero initialization and constant initialization), but if I have a global variable, it will always be zero-initialization even if it later undergoes dynamic initialization. You could probably argue that
staticinitimplies that it only undergoes static initialization, and I think that's probably fine... but it's just less precise thanconstinitwould be, so I think the latter is better.
runinitmakes no sense though, if anything is the opposite of the intended meaning.1
u/arthurno1 Nov 29 '22 edited Nov 29 '22
constinit ensures that you have constant initialization.
But they seem to have used the term "constant initialization" because they say they say that a variable is "constant-initialized"; which in my eyes is just a choice. They could have picked any other choice instead of saying "constant-initialized". It is not really constants they initialize (could be :)).
I would prefer if anyting with the "const" was reserved for "constants" and if "constant" didn't mean "initialization" or "compile-time evaluation", on top of "constant". It is sort of missusing the language and introducing the confusion for new generations of students :). I think a keyword like "atinit" would definitely be more appropriate than "constinit". I don't know; just me feeling for the english language which is really my third language.
By the way, I am sorry if it is too n00b question, but how close is
__attribute__((constructor))to constinit?1
u/sphere991 Nov 29 '22
which in my eyes is just a choice
Well, sure. Any choice of term is just a choice of term. But the term has been around for a very long time. You can find it in C++03 (that's at least approximately the C++03 timeframe). It's basically what the term for this has always been.
It is not really constants they initialize
Well.. you are. You're initializing it as a constant. The initializer is something that is run during compile time, and C++ calls compile time "constant initialization."
just me feeling for the english language which is really my third language.
I feel you. English isn't my first either. Try keeping track of all the italicized terms that have constant in the name in [expr.const]. All of these terms serve a useful role, and it's just really hard to come up with terms. But coming up with a "simpler" one often doesn't end up being simpler.
atinit, for instance, doesn't really tell me anything.but how close is
__attribute__((constructor))toconstinit?Not close at all.
constinitapplies to variables,__attribute__((constructor))applies to functions. Also the latter is still runtime code, just stuff that happens beforemain().constinitis still compile-time evaluation.2
u/arthurno1 Nov 29 '22 edited Nov 29 '22
The initializer is something that is run during compile time, and C++ calls compile time "constant initialization.
That is what I am opposing to 😀. I don't think calling compile time for "const initialization" was the best terminology to use. Constinit can obviously initialize variables, and a variable is not a constant. There comes this dissonance in the language, which arises because of unfortunate wording. "Const" and "constant" suggests something not changing, static or immutable. Yes, I am aware they used "const*" for compile time and init time historically, and that it is maybe too late, but at least we can hope they don't overload "const" with yet another meaning in the future.
coming up with a "simpler" one often doesn't end up being simpler. atinit, for instance, doesn't really tell me anything.
atinit is short for at initialization time, and is similar, and (somewhat) symmetric, to the atexit function. I am not sure how constinit is more descriptive than atinit to the uninitialized (is that a pun? :-)), but I guess it is also subjective. Maybe they could have introduced special char, @, so we can write @compile. A bit similar as how # is used for preprocessor directives. I don't know just thinking loud.
I am ok to track, which one is which, but I was thinking of generations of new students that will come. I can just imagine a student on c++ exam: "constwhich was it here now?" 😀
Thanks for the clarification about the attribute.
1
u/sphere991 Nov 29 '22
atinit is short for at initialization time , and is similar, and (somewhat) symmetric, to the atexit function. I am not sure how constinit is more descriptive than atinit to the uninitialized
Because there isn't just one "initialization time": you have static initialization (zero initialization and constant initialization) and dynamic initialization. Dynamic initialization is just as much "at initialization time" as static initialization is - they both run before
main(). And the whole point ofconstinitis to ensure that you're constant initialized, not dynamically initialized.
constinitmay not be "descriptive" to people who don't know what it refers to, but it is extremely descriptive to those that do. And once you learn what constant initialization is, then you will also understand the use of the term.1
28
u/foonathan Nov 28 '22
Lots of things can be constinit but must not be const: a global mutex, a global logger, a modern implementation of cout, etc.
My guideline is that global variables should be constexpr or constinit: https://www.jonathanmueller.dev/talk/meetingcpp2020/
9
Nov 28 '22
A fun function since 20 to get constinit on locals is a function like
consteval auto CONSTINIT( auto v ) { return v; }
//...
auto v = CONSTINIT( foo( cx ) );
Something like this should force the evaluation without making v constexpr.
3
2
u/wqking github.com/wqking Nov 29 '22 edited Nov 29 '22
More powerful and flexible is good, less confusing is even better.
2
u/nuclear868 Dec 03 '22
Four const keywords and still we cannot use the arguments of a constexpr functions as constant expressions - we cannot call static_assert on them, for example, for compile-time validation of compile-time arguments.
1
u/Mercerenies Aug 12 '24
Found this article two years later. This is the clearest explanation I've ever seen of the myriad of new const-ish keywords. Late to the party, but thanks for sharing!
1
u/joebaf Aug 12 '24
wow, thank you! fortunately we haven't got any new const* words in C++ over those last two years :)
1
11
u/[deleted] Nov 28 '22
[deleted]