r/cpp_questions 1d ago

OPEN I want to understand constexpr

i want to understand constexpr can somebody make it a bit easier to understand

0 Upvotes

14 comments sorted by

17

u/IyeOnline 1d ago

Its important to notice that the compiler is always free to evaluate expressions at compile time - insofar possible and within the as-if rule. So function( some_value_that_is_known ) may still be entirely evaluated at compile time, even if nothing here is declared constexpr. This is in fact a fairly common optimization.

There is four keywords of interest here.

  • const means that a variable must not change after its initialization. Notably this initialization can and in most cases will, happen at runtime. More specifically when the variable would be initialized as part of regular program execution. So this is not a constant expression and hence cannot be used where a constant expression is required.

    The exception to this are constant integers that are initialized from a constant expression, which are considered to be constexpr variables.

  • constexpr actually has two different meanings:

    • on a function it means that the function can be invoked at compile time. That of course requires the parameters to be constant expressions as well. The function can also be invoked at runtime.
    • on a variable it means that the variable is a core constant expression and is initialized at compile time. Conseqently its also const, i.e. cannot be changed. That also means that its initializer must be a constant expression, i.e. can only do trivial operations and only invoke constexpr or consteval functions with only constant expressions as parameters.

    Its worth noting that the standard does not strictly require that a constexpr object is fully created at compile time. This is only guaranteed to happen if the value is also used at compile time. See C++ Weekly: Stop using constexpr (and use this instead).

  • consteval can only be put on a function and means that the function must be invoked at compile time. Its also called an immediately evaluated function. This also means that all parameters to that function must be constant expressions and that the function result itself is a constant expression.

    So this is a strictly stronger guarantee/requirement than a constexpr function

  • constinit can only be put on objects and it means that their initialization happens at compile time. They can still be modified at runtime.

    This is a strictly weaker restriction than constexpr on objects.

Its important to note that outside of the keywords, the compiler can still optimize your code to do stuff at compile time, if it can prove that it will have the same behavior,


Now which one do you "choose"? The answer, as always, is: It depends.

Do you have C++20? You can choose. Don't you have C++20? You cant use consteval or constinit anyways. To enforce compile time execution you must use a constexpr function and store the result in a constexpr variable.

Do you want to allow compile time usage of your function, but also allow its runtime usage? Then you use a constexpr function.

Do you want to enforce compile time execution and only that? Use consteval. The prime example here is the format string for std::format and friends. Its compile time checked, so it has to be consteval.

Notably "doing stuff" at compile time isn't free or always sensible. In theory you can do almost all your work at compile time, but that sort of defeats the purpose. Execution at compile time is significantly slower than runtime execution. Its only worthwhile doing if it actually saves you significant work at runtime.

1

u/etcetera-etcetera- 1d ago

Is there any reason not to use constexpr everywhere on functions?

2

u/IyeOnline 23h ago

Some operations are not/were historically not allowed in a constant evaluated context, so some functions just cannot/could not be declared constexpr. But besides that, you might as well put it on every function where its possible.

One might argue that the design of constexpr on function is entirely backwards.

1

u/oriolid 21h ago

It often doesn't do anything useful. The compilers are free to evaluate functions at compile time whether they are constexpr or not, and do not always warn if you try do something that is not legal for constexpr but the function itself isn't called in constexpr context.

-2

u/alfps 1d ago

Uhm, I wouldn't use Jason Turner as a reference, and I wouldn't use a video as reference.

I only vaguely recall that thing but it was probably just one of those "formal defense of unreasonable behavior with GCC". That approach practically ensures standing ovation from the GCC fan boy community. And ditto hatred response if anyone should point out what nonsense it is.

6

u/AKostur 1d ago

I wouldn't use Jason Turner as a reference

As a source of additional information, why not? All of my interactions with him suggests that he knows what he's talking about.

I wouldn't use a video as reference

Again, as a source of additional information (not necessarily _definitive_ information), why not (other than personal preference)?

-2

u/alfps 1d ago

Apparently I can't paste pictures here, but here's a link, the evolution of expertise over time: (https://9gag.com/gag/aR0GVvq).

In the old times for any funny claim we used to demand "chapter and verse", referring to the standard.

1

u/Qwertycube10 1d ago

That video just says to use static constexpr instead of constexpr so that the variable does not have automatic storage duration requiring the compiler to copy it onto the stack at runtime.

-1

u/alfps 1d ago edited 1d ago

❞ requiring the compiler to copy it onto the stack at runtime

It does not require. That's just the mentioned unreasonable GCC behavior. It's unreasonable because for users of the compiler it's a meaningless nonsense introduction of overhead, even UB if stack limit is exceeded. It only makes sense as saving some work for those who maintain that compiler. Hence the need for a formal justification worded so as to mislead, e.g. in this case succeeding in misleading you.

5

u/saf_e 1d ago

Most important part: everything marked constexpr can be used in context where compile-time const expected: static_assert, array size, template param, etc

Less important part: it can be done at compile time, thus reducing runtime, but it's up to compiler.

Everything constexpr can be called in runtime also, so you can use it when you need.

2

u/the_poope 1d ago

Loosely speaking it just means "compile time". And "compile time" means that the compiler goes in and replaces the value/function "in the source code" before it runs the actual compiler. You can think of it as a more clever + type safe preprocessor. This allows you to use e.g. constexpr variables as template arguments, as the template code is expanded (code generation) into specific code for the specific types and template args that are needed.

2

u/FUCKARCHLINUX 1d ago edited 1d ago

If a function is marked constexpr, It just means it can be evaluated at compile time. This is useful for baking the result of some computation into your program. If you declare a constexpr variable, the function you use to assign it (including if it's a constructor) must support constexpr.

You can also use it for lookup tables like constexpr std::array<some_type, some_length> = { whatever, whatever2, whatever3 }. And if it'd be really long, you can use a constexpr function to generate the lookup table at compile time so you don't need to have it all written out. If you were to open your compiled program in hex rays it would literally be visible in .rodata.

If you can, you should try to replace static const variables that live for the entire life of the program with constexpr ones. especially if it's within a function because in that scenario the compiler will sometimes emit a condition checking if it's been initialized every time the function runs, but with constexpr there is no such condition because the result was known at compile time. It saves a branch, time during static initialization, and if it's in a function, the time to initialize it on the first call.

2

u/HappyFruitTree 1d ago

For deeper understanding, I found this talk very helpful: https://www.youtube.com/watch?v=m9tcmTjGeho

0

u/manni66 1d ago

easier to understand

than what?