TL;DR, we are talking about constant expressions, and this is the only specific guarantee about constant expressions and when they are evaluated that I can find, from [expr.const]: "[Note: Constant expressions can be evaluated during translation. - end note]" (can, not shall or must) ------- I have received some off-TH-cam comments questioning the validity of this episode. So here is a breakdown of the standard, plus some notes from Herb Sutter at the end. ------ First, let's look at what `constexpr` means, when applied to a variable, according to the standard: eel.is/c++draft/dcl.constexpr#10 [dcl.constexpr] A constexpr specifier used in an object declaration declares the object as const. Such an object shall have literal type and shall be initialized. In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression ([expr.const]). A constexpr variable shall have constant destruction. ------ It is const, and it is a "constant expression." No other guarantee is made. Important: constant expressions are not values computed at compile time. They are values that meet a specific set of requirements. From the standard: [expr.const] (eel.is/c++draft/expr.const#1) Certain contexts require expressions that satisfy additional requirements as detailed in this subclause; other contexts have different semantics depending on whether or not an expression satisfies these requirements. Expressions that satisfy these requirements, assuming that copy elision (11.10.5) is not performed, are called constant expressions. [Note: Constant expressions can be evaluated during translation. - end note] Note this statement "can be evaluated during translation." "Translation" is what we think of as "compilation" (eel.is/c++draft/lex.phases) This means there is no requirement at all when the values are computed, as long as it meets the requirements. ------ Next, let's look at the term "potentially-constant" eel.is/c++draft/expr.const#4 A variable is potentially-constant if it is constexpr or it has reference or const-qualified integral or enumeration type. A constant-initialized potentially-constant variable is usable in constant expressions at a point P if its initializing declaration D is reachable from P and -(4.1) it is constexpr, (there are many subclauses here, and it keeps going as you dig deeper into "potentially constant evaluated" "immediate invocation" "manifestly constant-evaluated" (which determines if "is_constant_evaluated" returns true) and "needed for constant evaluation"...) ------ So let's come back around to how `static` changes the situation: eel.is/c++draft/basic.start.static#2 [basic.start.static] Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized ([expr.const]). If constant initialization is not performed, a variable with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread]) is zero-initialized ([dcl.init]). Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization. All static initialization strongly happens before ([intro.races]) any dynamic initialization.
So, we are aiming for "constant initialization" which is a form of "static initialization." We need a "constant-initialized" "static" variable... ------- Phew, ok, that's what we got with `static constexpr` `constinit` was added in C++20 to also force this "static initialization" (the only form of initialization guaranteed to happen at compile-time). www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1143r2.html. Every initialization that is not "static initialization" is "dynamic initialization", which is not guaranteed to happen at compile-time. ([basic.start.static] comes up again). From what we can read, the only guarantee about a "constexpr" function or variable, is that it's "useable as a constant expression." -------- And here is the wording from Herb (isocpp.org/blog/2013/01/when-does-a-constexpr-function-get-evaluated-at-compile-time-stackoverflow) "Trying to summarize: constexpr isn't about forcing execution to happen at compile time always. It's about enabling programmers to more easily write functions and variables that, in addition to their normal uses, are also guaranteed to be legally usable where the language requires compile- time "constant expressions" (e.g., to initialize enumerator values and array bounds). The keyword "constexpr" is intended to connote that: "can be used in constant expressions." Granted, "constant expressions" is a precise standardese term, so we're always open to better ways of teaching this."
A good way to think about constant expressions is that `constexpr` is a hint that lets the compiler know that the variable or function *CAN* be evaluated at compile time, but it is not required (unless you, explicitly require it via constinit or NTTP or whatever). And another way to think about it is that the `value` of constexpr is calculated, but not the location. Kind of like using a regular literal, the compiler knows that there is a known value, but it will still need to copy it (or it's address) if you require so. constexpr controls the value, but not the location, when you set it to static you say that you want to have only one instance of that constant, thus the compiler can do more optimizations.
2018: Yo dawgs, use constexpr 2019: Guys, constexpr is cool 2020: Have you all heard of constexpr? 2021: Constexpr all the things 2022: Stop using `constexpr` Okay ... whatever you say, boss
this was indeed interesting. I think most C++ programmers know that constexpr doesn't connote a storage duration, but probably most like me were not as familiar with the standard as perhaps we should be to know to use this work-around to force constant evaluation, if possible (one way or the other - with or without optimization). To my mind it does show that C++ is more complicated than it really ought to be, and it shows that you can't assume that all compilers will generate code in even a similar way. It's a good idea to compile with address sanitization switched on if you can - I've been doing it for quite a while already and it has saved me a couple of times.
> I think most C++ programmers know that constexpr doesn't connote a storage duration it is a common misconception though, and in general even people who understand that don't understand that you are still asking the compiler to put things on the local stack.
What's sad is I've been aware of this "one neat trick" for some time, but it never seems to actually register in my head while the rough drafts are getting inked. This video just helped me to correct my ways.
Isn't this other way around? `constexpr` objects are real `const` objects, and you use address of it it will require to "materialize" object, like `constexpr int i = 3; foo(&i);` will cause to create `i` as stack variable.
Compiler explorer probably has additional guards that are counter intuitive, it will not tell you about stack overflows or other exceptions; It's important to keep this in mind when prototyping
C++ is such controlled flight into terrain. Can't speak for everyone, but all programmers that I know of just want robust compile-time execution - not this fanciful constructs. Jason is so secure in his job explaining to us how nonsensical C++ is.
You don't have much options either in domains where C++ is used. Rust is one option, but it isn't as close as C++ is. Also, as Rust is trying to be as good as C++ is, it's also becoming complex with each new addition!
Unfortunately you can't declare a variable as static within a constexpr function. This doesn't compile: constexpr int getVal(std::size_t in) { static std::array constexpr arr{0, 2, 4}; return arr[in]; }
Your example (declaring a variable within a narrow scope and pointing to it) is either academic or a case of strong code smell and has to be avoided. If you declare it static you can declare it out of the narrow scope anyway. And then you don't need static.
My example existed solely to point out that `constexpr` does not affect object lifetime. If you have a constexpr variable you are asking the compiler to copy data into the local stack, and it will often oblige. Sometimes the copy is eliminated by the optimizer
i don't see the point, the problem here is referring to something in a temporary scope, you did think about it too much IMO... you don't even need such a "complicated" setup to have the same results.
The point is that most people expect constexpr values to be static. they aren't. The rest of the people expect constexpr values to be computed at compile time... they aren't *required* to be. constexpr static solves both of those confusions.
@@cppweekly Thx for the added explanation, but as you said in the beginning of the video some of us would find that kinda obvious (and by far i'm not a c++ guru) Personally, the thing that really was painful to me, is that the introduced constexpr keyword, was not a forced and absolute compile time stuff, running ANY arbitrary code, and able to build structure/array/object at compile time That's the constexpr i needed !!!! The constexpr we have now is somewhat useless since advanced compilers could have done it already ?
@@ChaotikmindSrc That's an excellent point I was wondering myself. I still don't understand the difference between const and constexpr when used in constant declaration. My understanding is that constexpr in function declaration makes the compiler generate the result of the function during compile time and will make compiler error out if that's not possible which all makes sense to me. It's the use in constant declaration that escapes me. And btw. wouldn't static const have the same effect as static constexpr?
@@dejaniv constexpr is a "maybe" , not an obligation for the compiler, as Jason pointed many times static const will certainly result at least to some read only memory allocation at compile time, the mémory really exist at that address (bu t i'm unsure, the compiler can probably optimize that away) static constexpr can have the exact same effect i think but in practice, you get a bit more guarantee that it is optimized away, because you express the intent to the compiler. Also for functions, consider the case where it is obviously un optimizable ! I'll repeat myself : it is a bit frustrating IMO.
What would you suggest in case of a need for a static constexpr variable in a constexpr function? (considering that the constexpr function may in fact be called at runtime)
This is a great reminder of the difference between static constexpr and constexpr. ~~Nevertheless, static constexpr increases program startup time, thus sometimes you might prefer constexpr~~ Edit: I mixed up static with static constexpr
What do you mean, exactly, by "program startup time"? it does increase the size of the binary, which might increase load time, but it does 0 work at load time.
To the point that you need to run both Release and Debug builds: If you're building with both GCC and Clang, and don't want 2x2 jobs for just one OS, GCC gives more diagnostics at higher optimization levels, because some diagnostics come up during optimization. I'm not sure this happens with Clang, and it happened to show more diagnostics at no optimization in the video. For this reason, I would run GCC in Release mode and Clang in Debug. from the GCC docs, "Warning Options": > The effectiveness of some warnings depends on optimizations also being enabled. For example -Wsuggest-final-types is more effective with link-time optimization and some instances of other warnings may not be issued at all unless optimization is enabled. While optimization in general improves the efficacy of control and data flow sensitive warnings, in some cases it may also cause false positives.
you want the STATIC_REQUIRE capability that catch2 has. I use that in my C++ Starter Project github.com/cpp-best-practices/cpp_boilerplate_project/blob/main/test/constexpr_tests.cpp In the CMakeLists I show how you want to compile the tests twice. Once for runtime, once for constexpr usage: github.com/cpp-best-practices/cpp_boilerplate_project/blob/main/test/CMakeLists.txt#L21-L54
@@cppweekly ofc, that's reasonable. but I was just talking about forcing optimizer to evaluate at compile-time and the answer is consteval not static_assert()
*static* constexpr all the things, 100%. It's also clear that higher optimization levels force compilers to do more analysis and can generate more warnings. Compiling a codebase only in debug mode (without optimization) can miss some critical warnings (which you are hopefully treating as errors with -Werror).
I also wonder if lots of people want to use consteval instead of constexpr, even if it's initialized to a static variable. Consteval ensures that the value is computed at compile time, while constexpr can still take non-const variables and give a "constant expression" for calculation, but not a compile time calculation. For example, a constexpr can still use a non-const variable for control flow. Then there is hardly anything constexpr (in terms of run time complexity) on it, it's just normal code that maybe can be evaluated at compile time.
or you can leave all your functions constexpr (for flexibility) and then if you _really_ want something done at compile time, you can stick it in a consteval function (at the highest level) :)
4:49 this is what irritates me about constexpr, you say: "We haven't done anythign to require....", except of course that get_value() and the variable in main are both marked constexpr explicitly!
Technically not required unless you use it in a constexpr context. But it's reasonable to assume it will be calculated at compile time if assigned to a constexpr variable.
afaik it makes compilation somewhat slower, but may help you catch a few bugs, so it’s more used when you suspect there’s something going on with your code.
I think the keyword here is "can": constexpr variables _can_ be initialised at compile time, it's not a strict contract. Isn't that what constinit is for?
I found constexpr daunting first, but its actually quite simple: it just puts values into the binary. If you're using that value at some point during program execution, the instructions will still have to copy it in place
I have never used constexpr functions for the simple reason that if the calculation is too onerous to perform at run-time, I don’t want it to increase my compile time on _every compile_ either. My time is just as valuable as my customers. I precompute the values in a script or other short program and include them as a table of some sort. I’m sure this is an unpopular opinion.
It's not unpopular. Compile times are a real problem in C++, and not helped by all the compile time programming we do more and more (templates, constexpr). Some of this cost is alleviated by C++20 modules, thankfully.
I used it very … very rare. Reasons i still not learning about compile time calculations… i use only template function as meta programming… .I still like old fashion programming and is time to change. .very Good presentation.
This video is wrong. That value isn't a "stack value", it's an "automatic storage variable". This kind of terminology is harmful because it teaches people to use assembly reasoning on C++ code, instead of explicitly teaching them to use C++ reasoning. Machine reasoning works until it doesn't work. If you bother to learn the rules of C++ you would understand why this breaks (use of invalidated pointer to automatic storage variable after scope) and you might be able to better reason about the performance of code too. There is nothing wrong with using constexpr variables with automatic storage, the problem you demonstrate is completely unrelated to that.
It seems you chose one small bit of terminology to focus on and completely missed the point of the video. The point is that if you do not use static, you are explicitly asking the compiler to create a local copy for you in your current scope. Yes, it is an `auto`matic variable (let's go ahead and go back to the original C keyword for this discussion). And yes, the compiler *might* choose to optimize away that data copy. In many cases the compiler does not make this optimization. So you have missed the following points: 1) I teach C++. People regularly conflate `constexpr` with "object lifetime" (they are not related to each other in any way). Which is why I made this video, to dispel that myth 2) Local (automatic) copies of large blocks of constexpr computed data can be a significant performance bottleneck if you are not careful 3) I have published several puzzle books with the explicit goal of helping C++ programmers understand the different types of object lifetime and their implications in code: leanpub.com/u/jason_turner
But you keep suggesting to think of this in terms of stack/heap when such concepts do not exist in C++ instead of using C++ terminology. Pointing out that "constexpr" is not a storage duration is enough to solve the problem. The same issue would happen if you did not use constexpr. What that means is, constexpr is unrelated to the problem. Instead of "use static constexpr" which is bad advice, you could have just pointed out the correct usage of automatic storage variables is not influenced by constexpr. An example, I see a lot of people in my industry think they can do things like use "volatile bool" to synchronize different threads, (which you cannot), which is a direct result of people being taught to think of things in terms of machine or assembly language concepts. There are subtle differences between C++ concepts and the machine concepts. As an example where C++ and machine logic differ, a volatile write in C++ compiles (in isolation) to a store instruction on x86, which has release semantics. However, from the C++ perspective, the release does not exist which allows compiler optimizations that can break things if you think of C++ as equivalent to the generated machine code. Some people even have the audacity to call it a "false positive" in ThreadSanitizer because they simply do not understand the rules of C++. It is not entirely the same, but it's just another example of the harm that results from people using machine/assembly logic instead of C++ logic. The subtle differences between the rules of C++ and machine code are what break things and we all need to teach people to avoid the train of thinking of C++ as an abstraction of machine code (because it is not). We can help people by using C++ terminology and not machine code terminology so they maintain the right frame of reference and maybe actually go read up and understand how things work. But instead all you are doing here is providing a mythical guideline as a substitute for teaching the underlying concept (C++ storage durations and particularly how they interact with constexpr). So yes, this terminology does matter, because now people will go Google "stack" instead of "C++ storage duration" and not be exposed to the formalism of the language.
They are irrelevant to this video. This video is about constexpr vs static constexpr. consteval is applied to functions, and constinit requires static already.
14:49 What do you mean "You almost always mean 'static constexpr' "? I don't understand... I don't want a static variable (a variable that lives until the program terminates). I want a variable evaluated at compile time, which is what 'constexpr' provides. For variables. For functions, "constexpr" is more of a hint to the compiler. If you really want to enforce compile-time evaluation for a function you use 'consteval' (introduced in C++20). 16:49-17:00 That's why you mark it as 'constinit' (introduced in C++20). Then yes... it needs to be static.
You very very rarely mean "I want this to be evaluated at compile time (constexpr) and I want a copy of it to live on the current stack (non-static)" You generally mean "I want this to be evaluated at compile time, and I don't care where it lives" Non-static can be a real performance sink if you have a large constexpr object that has to be copied unto the stack every time a function is called. I have measured this impact in real code.
constinit is required to be static. So constinit x = some_func(); // is not valid static constinit const x = some_func(); is just a really expressive way of spelling: static constexpr x = some_func();
On gcc -O0 copying the array for "constexpr auto values = get_values()", could we expect the compiler would actually do return value optimization? I understand that the best solution here is to use static constexpr, I was just wondering if constexpr could have some other interaction with RVO.
Why would I use a pointer on a local array that is returned by a constexpr function? This doesn't make sense. You are over complicating things. Replace #define and const with (non-static) constexpr (for simple scalar values), that's all - a lot of legacy as well as new code is still using #define and consts, and not constexpr, and you're discouraging them to use constexpr with this complex and weird example?
@@cppweekly I did watch the entire video, and that's why I'm saying this. You're suggesting to fix something which isn't broken. It seems more like a dangling pointer issue, and not a constexpr issue.
TLDR: Do not blindly apply `constexpr` to your code without giving each variable or function definition some thought. The real reason to stop using `constexpr` is because it is a parasitic feature that forces your API to be publicly implemented. This is a big "Yikes!!!" because you cannot later un-`constexpr`ify your API without breaking client code. (Not to mention sinful compile times of header-only libraries and other real-world requirements, such as not wanting to publish source code.) I'm all down for templates and such, but as a strong rule-of-thumb I opt into them for semantic power, not for optimization reasons. I apply these exact same principles to non-local non-private `constexpr` declarations. 90% of the time I only declare something `constexpr` if I have to pass the value as an argument to a template or something like that.
This shows that the C++ language is getting more unintuitive keywords/extension, and way worse - the diagnostics are now intentionally broken. What is up with all the "no diagnostic needed"? The standard is literally telling us that the compiler KNOWS that the code is not correct but to not tell the developer!?!? And when you do get an error-message during compilation it is either very cryptic, extremely long and cryptic (looking at you templates), or outright wrong like in this case at 10:46.
I see this problem you experienced would happen without constexpr. Is this video due to the unresolved emotional issues, in taking 5 years to fix a constexpr problem? I see that if you heal the unresolved emotional issues, you’ll see things clearer and be able to take responsibility.
5:13 Why are you calling Clang "LLVM"? It's an acronym for "low level virtual machine", but then apparently some "Project LLVM" came up during the 2000's which apparently is a toolkit for building compilers. A collection of modular and reusable compiler and toolchain technologies. 😐 Fucking hell...
TL;DR, we are talking about constant expressions, and this is the only specific guarantee about constant expressions
and when they are evaluated that I can find, from [expr.const]: "[Note: Constant expressions can be evaluated during translation. - end note]" (can, not shall or must)
-------
I have received some off-TH-cam comments questioning the validity of this episode. So here is a breakdown
of the standard, plus some notes from Herb Sutter at the end.
------
First, let's look at what `constexpr` means, when applied to a variable, according to the standard: eel.is/c++draft/dcl.constexpr#10
[dcl.constexpr]
A constexpr specifier used in an object declaration declares the object as const.
Such an object shall have literal type and shall be initialized.
In any constexpr variable declaration, the full-expression of the initialization shall be a constant expression ([expr.const]).
A constexpr variable shall have constant destruction.
------
It is const, and it is a "constant expression." No other guarantee is made.
Important: constant expressions are not values computed at compile time. They are values that meet a specific set of requirements.
From the standard:
[expr.const] (eel.is/c++draft/expr.const#1)
Certain contexts require expressions that satisfy additional requirements as detailed in this subclause; other
contexts have different semantics depending on whether or not an expression satisfies these requirements.
Expressions that satisfy these requirements, assuming that copy elision (11.10.5) is not performed, are called
constant expressions. [Note: Constant expressions can be evaluated during translation. - end note]
Note this statement "can be evaluated during translation." "Translation" is what we think of as "compilation" (eel.is/c++draft/lex.phases)
This means there is no requirement at all when the values are computed, as long as it meets the requirements.
------
Next, let's look at the term "potentially-constant" eel.is/c++draft/expr.const#4
A variable is potentially-constant if it is constexpr or it has reference or const-qualified integral or enumeration
type.
A constant-initialized potentially-constant variable is usable in constant expressions at a point P if its
initializing declaration D is reachable from P and
-(4.1) it is constexpr,
(there are many subclauses here, and it keeps going as you dig deeper into "potentially constant evaluated" "immediate invocation" "manifestly constant-evaluated" (which determines if "is_constant_evaluated" returns true) and "needed for constant evaluation"...)
------
So let's come back around to how `static` changes the situation:
eel.is/c++draft/basic.start.static#2
[basic.start.static]
Constant initialization is performed if a variable or temporary object with static or thread storage duration is constant-initialized ([expr.const]).
If constant initialization is not performed, a variable with static storage duration ([basic.stc.static]) or thread storage duration ([basic.stc.thread])
is zero-initialized ([dcl.init]).
Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization.
All static initialization strongly happens before ([intro.races]) any dynamic initialization.
So, we are aiming for "constant initialization" which is a form of "static initialization." We need a "constant-initialized" "static" variable...
-------
Phew, ok, that's what we got with `static constexpr`
`constinit` was added in C++20 to also force this "static initialization" (the only form of initialization guaranteed to happen at compile-time). www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1143r2.html. Every initialization that is not "static initialization" is "dynamic initialization", which is not guaranteed to happen at compile-time. ([basic.start.static] comes up again).
From what we can read, the only guarantee about a "constexpr" function or variable, is that it's "useable as a constant expression."
--------
And here is the wording from Herb (isocpp.org/blog/2013/01/when-does-a-constexpr-function-get-evaluated-at-compile-time-stackoverflow)
"Trying to summarize: constexpr isn't about forcing execution to happen at compile time always. It's about enabling programmers to more easily
write functions and variables that, in addition to their normal uses, are also guaranteed to be legally usable where the language requires compile-
time "constant expressions" (e.g., to initialize enumerator values and array bounds).
The keyword "constexpr" is intended to connote that: "can be used in constant expressions." Granted, "constant expressions" is a precise
standardese term, so we're always open to better ways of teaching this."
A good way to think about constant expressions is that `constexpr` is a hint that lets the compiler know that the variable or function *CAN* be evaluated at compile time, but it is not required (unless you, explicitly require it via constinit or NTTP or whatever).
And another way to think about it is that the `value` of constexpr is calculated, but not the location. Kind of like using a regular literal, the compiler knows that there is a known value, but it will still need to copy it (or it's address) if you require so. constexpr controls the value, but not the location, when you set it to static you say that you want to have only one instance of that constant, thus the compiler can do more optimizations.
This is the real answer.
@@thomassynths As usual, almost every keyword in C++ is just a HINT to the compiler of intention, it will always do it's own thing in the end.
Maybe every single keyword in c++ can be a dangerous trap eventually.
Which keyword isn't already a dangerous trap?
@@Nobody1707 even things like "int" can be dangerous if you know nothing about signed overflow undefined behavior.
2018: Yo dawgs, use constexpr
2019: Guys, constexpr is cool
2020: Have you all heard of constexpr?
2021: Constexpr all the things
2022: Stop using `constexpr`
Okay ... whatever you say, boss
nice meme
constexpr all the things was 2017. th-cam.com/video/HMB9oXFobJc/w-d-xo.html
C++ never fails to surprise you... Great video
Because its Sea(C) underneath
this was indeed interesting. I think most C++ programmers know that constexpr doesn't connote a storage duration, but probably most like me were not as familiar with the standard as perhaps we should be to know to use this work-around to force constant evaluation, if possible (one way or the other - with or without optimization).
To my mind it does show that C++ is more complicated than it really ought to be, and it shows that you can't assume that all compilers will generate code in even a similar way.
It's a good idea to compile with address sanitization switched on if you can - I've been doing it for quite a while already and it has saved me a couple of times.
> I think most C++ programmers know that constexpr doesn't connote a storage duration
it is a common misconception though, and in general even people who understand that don't understand that you are still asking the compiler to put things on the local stack.
And there I was expecting "consteval"... still good feedback on the use of static. Thanks
I was too.. And I've been told before static variables are evil.. Now what to think?
@@kodref non-const statics should be avoided in most situations, but there is nothing wrong with const statics
@@anon8510 mutable global state is notoriously error-prone
What's sad is I've been aware of this "one neat trick" for some time, but it never seems to actually register in my head while the rough drafts are getting inked. This video just helped me to correct my ways.
This is normal with C++. It's so huge it's like trying to memorise the whole of Homer's Odyssey by heart....
> _"Oh OK, going for Clickbait today?"_
thanks a lot for mentioning that in the thumbnail
Isn't this other way around? `constexpr` objects are real `const` objects, and you use address of it it will require to "materialize" object, like `constexpr int i = 3; foo(&i);` will cause to create `i` as stack variable.
Compiler explorer probably has additional guards that are counter intuitive, it will not tell you about stack overflows or other exceptions; It's important to keep this in mind when prototyping
Thanks, now I will pay more attention to constexpr.
The one point I’m missing: when would the compiler do things differently is you used const instead of constexpr?
C++ is such controlled flight into terrain.
Can't speak for everyone, but all programmers that I know of just want robust compile-time execution - not this fanciful constructs.
Jason is so secure in his job explaining to us how nonsensical C++ is.
You don't have much options either in domains where C++ is used. Rust is one option, but it isn't as close as C++ is. Also, as Rust is trying to be as good as C++ is, it's also becoming complex with each new addition!
@@sirnawazthings can be complex without being nonsensical like c++
Jason 2021: Use constexpr!
Jason 2022: Don't use constexpr!
Unfortunately you can't declare a variable as static within a constexpr function. This doesn't compile:
constexpr int
getVal(std::size_t in) {
static std::array constexpr arr{0, 2, 4};
return arr[in];
}
Stay tuned for next week. I found a workaround for that. Next week's episode is very important.
Is there any case where you prefer `constexpr` over `static constexpr`?
my thoughts exactly; idk why they didn't make static the default...
Your example (declaring a variable within a narrow scope and pointing to it) is either academic or a case of strong code smell and has to be avoided. If you declare it static you can declare it out of the narrow scope anyway. And then you don't need static.
My example existed solely to point out that `constexpr` does not affect object lifetime.
If you have a constexpr variable you are asking the compiler to copy data into the local stack, and it will often oblige. Sometimes the copy is eliminated by the optimizer
Who would have thought high level languages can make more sense when you see the assembly? The title is fine.
One interesting wrinkle, `constexpr static` member variables are implicitly inline, but non-member `constexpr static` are not implicitly inline.
And that is a huge help in many cases.
i don't see the point, the problem here is referring to something in a temporary scope, you did think about it too much IMO...
you don't even need such a "complicated" setup to have the same results.
The point is that most people expect constexpr values to be static. they aren't. The rest of the people expect constexpr values to be computed at compile time... they aren't *required* to be.
constexpr static solves both of those confusions.
@@cppweekly Thx for the added explanation, but as you said in the beginning of the video some of us would find that kinda obvious
(and by far i'm not a c++ guru)
Personally, the thing that really was painful to me, is that the introduced constexpr keyword, was not a forced and absolute compile time stuff, running ANY arbitrary code, and able to build structure/array/object at compile time
That's the constexpr i needed !!!!
The constexpr we have now is somewhat useless since advanced compilers could have done it already ?
@@ChaotikmindSrc That's an excellent point I was wondering myself. I still don't understand the difference between const and constexpr when used in constant declaration. My understanding is that constexpr in function declaration makes the compiler generate the result of the function during compile time and will make compiler error out if that's not possible which all makes sense to me. It's the use in constant declaration that escapes me. And btw. wouldn't static const have the same effect as static constexpr?
@@dejaniv constexpr is a "maybe" , not an obligation for the compiler, as Jason pointed many times
static const will certainly result at least to some read only memory allocation at compile time, the mémory really exist at that address (bu t i'm unsure, the compiler can probably optimize that away)
static constexpr can have the exact same effect i think but in practice, you get a bit more guarantee that it is optimized away, because you express the intent to the compiler.
Also for functions, consider the case where it is obviously un optimizable !
I'll repeat myself : it is a bit frustrating IMO.
What would you suggest in case of a need for a static constexpr variable in a constexpr function? (considering that the constexpr function may in fact be called at runtime)
GREAT question!!
Remember how I said I needed this episode because it's background for next weeks episode? Stay tuned for next week!
This is a great reminder of the difference between static constexpr and constexpr.
~~Nevertheless, static constexpr increases program startup time, thus sometimes you might prefer constexpr~~
Edit: I mixed up static with static constexpr
What do you mean, exactly, by "program startup time"?
it does increase the size of the binary, which might increase load time, but it does 0 work at load time.
@@cppweekly you are right, I mixed up static constexpr with static with will run constructors before main
To the point that you need to run both Release and Debug builds:
If you're building with both GCC and Clang, and don't want 2x2 jobs for just one OS, GCC gives more diagnostics at higher optimization levels, because some diagnostics come up during optimization. I'm not sure this happens with Clang, and it happened to show more diagnostics at no optimization in the video. For this reason, I would run GCC in Release mode and Clang in Debug.
from the GCC docs, "Warning Options":
> The effectiveness of some warnings depends on optimizations also being enabled. For example -Wsuggest-final-types is more effective with link-time optimization and some instances of other warnings may not be issued at all unless optimization is enabled. While optimization in general improves the efficacy of control and data flow sensitive warnings, in some cases it may also cause false positives.
what about consteval?
Stay tuned for 2 weeks from now. Ep 314
Dear Jason, I immensely enjoyed your talk on constexpr, truly eye-opening. Are you aware of any fully constexpr unit testing framework?
you want the STATIC_REQUIRE capability that catch2 has. I use that in my C++ Starter Project github.com/cpp-best-practices/cpp_boilerplate_project/blob/main/test/constexpr_tests.cpp
In the CMakeLists I show how you want to compile the tests twice. Once for runtime, once for constexpr usage:
github.com/cpp-best-practices/cpp_boilerplate_project/blob/main/test/CMakeLists.txt#L21-L54
if you want to force the optimizer to evaluate at compile-time just use "consteval", don't use static_assert()
Assuming you have C++20 and your function is consteval. Not all functions are, or should be, consteval.
I cover that in 2 episodes.
@@cppweekly ofc, that's reasonable. but I was just talking about forcing optimizer to evaluate at compile-time and the answer is consteval not static_assert()
*static* constexpr all the things, 100%. It's also clear that higher optimization levels force compilers to do more analysis and can generate more warnings. Compiling a codebase only in debug mode (without optimization) can miss some critical warnings (which you are hopefully treating as errors with -Werror).
I also wonder if lots of people want to use consteval instead of constexpr, even if it's initialized to a static variable. Consteval ensures that the value is computed at compile time, while constexpr can still take non-const variables and give a "constant expression" for calculation, but not a compile time calculation.
For example, a constexpr can still use a non-const variable for control flow. Then there is hardly anything constexpr (in terms of run time complexity) on it, it's just normal code that maybe can be evaluated at compile time.
in two weeks (314) I address this issue.
or you can leave all your functions constexpr (for flexibility) and then if you _really_ want something done at compile time, you can stick it in a consteval function (at the highest level) :)
This was really awesome ..how about making a consteval function that returns a compile time value ?
we'll cover that in 2 episodes
awesome 👍👍👏👏
Why not declare all const instead of constexpr and let the compiler decide what to do? That's why we have the -O3 option.
4:49 this is what irritates me about constexpr, you say: "We haven't done anythign to require....", except of course that get_value() and the variable in main are both marked constexpr explicitly!
What about constexpr values and functions used in template arguments?
Thanks
Compilers implement block scope static variables with some sort of atomic mutex. Are there any performance costs associated with these?
Good question. They should not as it’s initialised at compile time. No need all the lazy init checks and overhead.
I just need to ask for explanation here, if we declared constexpr variable, isnt initialisation of, guaranteed to be executed in compile time?
Technically not required unless you use it in a constexpr context. But it's reasonable to assume it will be calculated at compile time if assigned to a constexpr variable.
what does the -fsantize=address actually do and why not always use it?
afaik it makes compilation somewhat slower, but may help you catch a few bugs, so it’s more used when you suspect there’s something going on with your code.
The compiler injects a whole load of runtime checks for stuff like use-after-free. It adds about 10% overhead on normal programs.
I think the keyword here is "can": constexpr variables _can_ be initialised at compile time, it's not a strict contract. Isn't that what constinit is for?
constinit is required to be static. Stay tuned for episode 314
@@cppweekly Yes, you're absolutely right (of course...). Just checked cppreference. Waiting for episode 314.
I found constexpr daunting first, but its actually quite simple: it just puts values into the binary. If you're using that value at some point during program execution, the instructions will still have to copy it in place
I've heard the counter argument that locals are more likely to be optimized away entirely compared to statics.
Probably from me, but `constexpr` changes the nature of the game a bit here too.
I have never used constexpr functions for the simple reason that if the calculation is too onerous to perform at run-time, I don’t want it to increase my compile time on _every compile_ either. My time is just as valuable as my customers. I precompute the values in a script or other short program and include them as a table of some sort.
I’m sure this is an unpopular opinion.
It's not unpopular. Compile times are a real problem in C++, and not helped by all the compile time programming we do more and more (templates, constexpr). Some of this cost is alleviated by C++20 modules, thankfully.
Is it the same if you use ccache ?
video starts 1:45
Would moving all constexpr variables to static cause slower performance at start up?
Isn't it just baked into the binary though? If anything I think it would be faster because there's no stack allocation.
I used it very … very rare. Reasons i still not learning about compile time calculations… i use only template function as meta programming…
.I still like old fashion programming and is time to change.
.very Good presentation.
This video is wrong. That value isn't a "stack value", it's an "automatic storage variable". This kind of terminology is harmful because it teaches people to use assembly reasoning on C++ code, instead of explicitly teaching them to use C++ reasoning. Machine reasoning works until it doesn't work. If you bother to learn the rules of C++ you would understand why this breaks (use of invalidated pointer to automatic storage variable after scope) and you might be able to better reason about the performance of code too. There is nothing wrong with using constexpr variables with automatic storage, the problem you demonstrate is completely unrelated to that.
It seems you chose one small bit of terminology to focus on and completely missed the point of the video.
The point is that if you do not use static, you are explicitly asking the compiler to create a local copy for you in your current scope. Yes, it is an `auto`matic variable (let's go ahead and go back to the original C keyword for this discussion). And yes, the compiler *might* choose to optimize away that data copy.
In many cases the compiler does not make this optimization.
So you have missed the following points:
1) I teach C++. People regularly conflate `constexpr` with "object lifetime" (they are not related to each other in any way). Which is why I made this video, to dispel that myth
2) Local (automatic) copies of large blocks of constexpr computed data can be a significant performance bottleneck if you are not careful
3) I have published several puzzle books with the explicit goal of helping C++ programmers understand the different types of object lifetime and their implications in code: leanpub.com/u/jason_turner
But you keep suggesting to think of this in terms of stack/heap when such concepts do not exist in C++ instead of using C++ terminology. Pointing out that "constexpr" is not a storage duration is enough to solve the problem. The same issue would happen if you did not use constexpr. What that means is, constexpr is unrelated to the problem. Instead of "use static constexpr" which is bad advice, you could have just pointed out the correct usage of automatic storage variables is not influenced by constexpr.
An example, I see a lot of people in my industry think they can do things like use "volatile bool" to synchronize different threads, (which you cannot), which is a direct result of people being taught to think of things in terms of machine or assembly language concepts. There are subtle differences between C++ concepts and the machine concepts.
As an example where C++ and machine logic differ, a volatile write in C++ compiles (in isolation) to a store instruction on x86, which has release semantics. However, from the C++ perspective, the release does not exist which allows compiler optimizations that can break things if you think of C++ as equivalent to the generated machine code. Some people even have the audacity to call it a "false positive" in ThreadSanitizer because they simply do not understand the rules of C++. It is not entirely the same, but it's just another example of the harm that results from people using machine/assembly logic instead of C++ logic. The subtle differences between the rules of C++ and machine code are what break things and we all need to teach people to avoid the train of thinking of C++ as an abstraction of machine code (because it is not).
We can help people by using C++ terminology and not machine code terminology so they maintain the right frame of reference and maybe actually go read up and understand how things work. But instead all you are doing here is providing a mythical guideline as a substitute for teaching the underlying concept (C++ storage durations and particularly how they interact with constexpr).
So yes, this terminology does matter, because now people will go Google "stack" instead of "C++ storage duration" and not be exposed to the formalism of the language.
No mention about constinit and consteval?
They are irrelevant to this video. This video is about constexpr vs static constexpr. consteval is applied to functions, and constinit requires static already.
Oh of course I knew exactly what constexpr did this entire time!
;)
great one.
which pdf reader does you use
Usually just Microsoft Edge. It's actually really good to use with pen interfaces too.
Title: C++ in a nutshell
14:49 What do you mean "You almost always mean 'static constexpr' "?
I don't understand... I don't want a static variable (a variable that lives until the program terminates). I want a variable evaluated at compile time, which is what 'constexpr' provides. For variables.
For functions, "constexpr" is more of a hint to the compiler. If you really want to enforce compile-time evaluation for a function you use 'consteval' (introduced in C++20).
16:49-17:00 That's why you mark it as 'constinit' (introduced in C++20). Then yes... it needs to be static.
You very very rarely mean "I want this to be evaluated at compile time (constexpr) and I want a copy of it to live on the current stack (non-static)"
You generally mean "I want this to be evaluated at compile time, and I don't care where it lives"
Non-static can be a real performance sink if you have a large constexpr object that has to be copied unto the stack every time a function is called. I have measured this impact in real code.
Actually is that the same as constinit const?
constinit is required to be static.
So
constinit x = some_func(); // is not valid
static constinit const x = some_func();
is just a really expressive way of spelling:
static constexpr x = some_func();
On gcc -O0 copying the array for "constexpr auto values = get_values()", could we expect the compiler would actually do return value optimization?
I understand that the best solution here is to use static constexpr, I was just wondering if constexpr could have some other interaction with RVO.
RVO tends to happen regardless of optimization level, because it is part of the ABI.
Hahahahahaha loved the sarcastic clickbait title!
Thanks ! Unsubscribed.
Why would I use a pointer on a local array that is returned by a constexpr function? This doesn't make sense. You are over complicating things. Replace #define and const with (non-static) constexpr (for simple scalar values), that's all - a lot of legacy as well as new code is still using #define and consts, and not constexpr, and you're discouraging them to use constexpr with this complex and weird example?
You seem to have not actually finished the video. The point is that you should prefer `static constexpr` over `constexpr`.
@@cppweekly I did watch the entire video, and that's why I'm saying this. You're suggesting to fix something which isn't broken. It seems more like a dangling pointer issue, and not a constexpr issue.
Cool video, but my eyes are literally burning, could you turn on dark theme?
I've been sorting out how to get a decent looking dark theme in CE. I think I finally have what I want.
I think it should be called "Stop passing pointers of temporary variables outside of its scope"
That was only just a tiny part of the point of this video. I needed to illustrate that constexpr does not affect object lifetime.
just write "and use static constexpr instead" in the title
TLDR: Do not blindly apply `constexpr` to your code without giving each variable or function definition some thought.
The real reason to stop using `constexpr` is because it is a parasitic feature that forces your API to be publicly implemented. This is a big "Yikes!!!" because you cannot later un-`constexpr`ify your API without breaking client code. (Not to mention sinful compile times of header-only libraries and other real-world requirements, such as not wanting to publish source code.)
I'm all down for templates and such, but as a strong rule-of-thumb I opt into them for semantic power, not for optimization reasons. I apply these exact same principles to non-local non-private `constexpr` declarations. 90% of the time I only declare something `constexpr` if I have to pass the value as an argument to a template or something like that.
Use macros :)
I hate those stupid titles.
I knew the video would be good, otherwise I'd have ignored it with disdain.
Sorry, TH-cam makes me play the game sometimes.
Wow this video is very bad, sry.
This shows that the C++ language is getting more unintuitive keywords/extension, and way worse - the diagnostics are now intentionally broken.
What is up with all the "no diagnostic needed"?
The standard is literally telling us that the compiler KNOWS that the code is not correct but to not tell the developer!?!? And when you do get an error-message during compilation it is either very cryptic, extremely long and cryptic (looking at you templates), or outright wrong like in this case at 10:46.
NDR is not to confuse the developer but because in general it would be impossible or prohibitively expensive to diagnose certain issues.
I see this problem you experienced would happen without constexpr.
Is this video due to the unresolved emotional issues, in taking 5 years to fix a constexpr problem? I see that if you heal the unresolved emotional issues, you’ll see things clearer and be able to take responsibility.
5:13 Why are you calling Clang "LLVM"?
It's an acronym for "low level virtual machine", but then apparently some "Project LLVM" came up during the 2000's which apparently is a toolkit for building compilers. A collection of modular and reusable compiler and toolchain technologies. 😐 Fucking hell...
just don't use C++
its a horrid language
use C instead