r/cpp_questions • u/Slow_Negotiation_935 • 1d ago
OPEN Guidelines for constexpr
I’ve working with C++23 and I’m making some constructors/methods constexpr and seeing improvements in performance. So far so good. The problem is I don’t want to apply it ‘everywhere’. Is there a good source of advice , or anyone got any good tips?
5
u/bert8128 1d ago
What’s wrong with everywhere? Is there a downside?
6
u/oriolid 1d ago
Clutter, and the fact that it often doesn't do anything useful. Compilers can evaluate functions at compile time as part of normal optimization, and on the other hand constexpr doesn't require compile time evaluation.
1
u/bert8128 1d ago
Why is it clutter any more than const is clutter? It gives a benefit - it guarantees that it can be called at compile time and if you you change the code so this is no longer true then you get a compile error.
Constexpr also guarantees no UB.3
1
u/HappyFruitTree 1d ago
You only have these guarantees for the set of argument values that you actually call the function with from constexpr contexts.
int bar(int x) { return x; } constexpr int foo(int x) { if (x > 0) return bar(x); return 0; } int x = foo(5); // OK constexpr int y = foo(0); // OK constexpr int z = foo(5); // Error3
u/etaithespeedcuber 1d ago
One issue is that C++ sucks and you can't write exposed constexpr functions in source files, only in headeds. Though realistically if you do something like: foo.hpp
size_t resolve_size();foo.cpp ```include "foo.hpp"
size_t resolve_size() { constexpr size_t result = resolve_size_constexpr_impl(); return result; } ``` The compiler will probably output exactly the same thing. But you still won't be able to use it in other constexpr functions
1
u/bert8128 1d ago
So if we narrow the scope to only those defined in the header is there a downside?
1
u/etaithespeedcuber 1d ago
I mean, I think at that point it's just the limitations of the language. For example, you can't have a constexpr function that returns an object that is used directly at runtime if that object depends on the heap
2
u/HappyFruitTree 1d ago
You can, you just won't be able to call it from constexpr contexts (at compile time).
The following compiles and runs just fine (since C++20).
#include <vector> #include <iostream> constexpr std::vector<int> f() { std::vector<int> vec = {1, 2, 3}; return vec; } int main() { std::vector<int> vec = f(); for (int n : vec) { std::cout << n << "\n"; } }1
u/etaithespeedcuber 1d ago
How does it work? Does it convert it to an underlying static std::span? Or does it just literally run the function at runtime?
2
u/HappyFruitTree 1d ago
Yes,
vecin main is not declared constexpr so it will basically just compile and optimize it the same way as a normal function.2
u/meancoot 1d ago
Evaluates as if done at run time. This is always the case for
constexprfunctions, unless they are used in a context which requires a constant expression they behave as if executed at runtime. This is observable when you add C++20'sstd::is_constant_evaluatedinto the mix.https://godbolt.org/z/Ynxo6Evx1
#include <type_traits> constexpr bool constexpr_function() { return std::is_constant_evaluated(); } int main() { constexpr bool result = constexpr_function(); // <- result = true // bool result = constexpr_function(); // <- result = false return result; }
constevalworks around this by requiring a marked function to always execute at compile time, but the all of the arguments to such functions are required to be constant expressions.2
u/HappyFruitTree 20h ago edited 19h ago
Constant evaluation can also happen when initializing static variables and local const integer variables (if constant evaluation fails it falls back to normal "runtime" evaluation). https://godbolt.org/z/qE8PEq18r
4
u/Nils_Meul 1d ago
I don’t think there’s any downside to putting it everywhere it can go. Of course if you start making all your code header only just to take advantage of constexpr that’s a different question.
2
u/HappyFruitTree 1d ago
Even on functions that can not be executed in constexpr contexts?
int bar(int x); constexpr int foo(int x) { return bar(x); }2
u/Nils_Meul 1d ago
That’s the thing, in this case constexpr just doesn’t do anything (to my knowledge). The compiler isn’t forced to evaluate it at compile time even if it could, other than with constexpr variables…
5
u/HappyFruitTree 1d ago
Then why add it?
My main concern is that it can be confusing for the programmer (code is not only written for compilers). If I see a function marked
constexprI normally assume that it can result in a constant expression for at least some arguments.That constexpr functions had to be able to produce a constant expression for at least one set of argument values was actually a requirement before C++23 (no diagnostic required).
3
u/Nils_Meul 1d ago
True, valid point. But I was thinking more so about putting it wherever it can in principle run at compile time, and if you mistakenly put it somewhere it can’t that’s of course not optimal but not terrible either.
1
u/oriolid 1d ago
Just have a look at this: https://godbolt.org/z/8Gsd13sv8. In C++11 std::sin isn't constexpr, and wrap_sin not constexpr just to make sure. It still gets evaluated at compile time.
3
u/Nils_Meul 1d ago
Sure, the compiler is always allowed to evaluate something at compile-time. But if you want to assign the value of sin(1.0) to a constexpr variable you‘ll need a constexpr std::sin (at least I‘m pretty sure that you do) even though the compiler can figure out it’s value as shown in your example.
1
u/oriolid 1d ago
I know. The example was chosen to show that *not* adding constexpr does not prevent compiler from compile time evaluation. If you actually need the result in constexpr context, then you of course need to have the funtion as constexpr.
So, putting it wherever ever you can does not really do anything useful. There are more fun ways to waste time.
1
2
u/SyntheticDuckFlavour 1d ago
My general rule is, if you create reusable libraries where it may be applied in, or make sense in a constexpr context, then invest in making your functions constexpr. For example, make your math libraries constexpr wherever possible. For objects and APIs whose role is mutating state, don't bother. For example, state machines in a game, or something.
1
u/Normal-Narwhal0xFF 1d ago
I don't think constexpr changes a lot of the ways one would optimize. First, measure and find the code that's taking the time. Optimizing other code will keep you busy but not give results. Only focus on observed hot spots. Then decide if there's values being computed repeatedly that never change. If so they may benefit from constexpr. If code isn't inlining, and you think it'd help the hot spots, maybe constexpr can help there too. But make sure you're actually getting compile time resolution. Constexpr functions can still be runtime if they aren't used in constant expression context. So ensure it's giving you benefit of else it's just messing around. Measure, observe, and measure again. Without good data and feedback, you won't get results and will mostly waste time and even possibly introduce bugs with needless change.
1
u/RazzmatazzLatter8345 8h ago
Numerical types (particularly without heap allocation) is where I would especially try to make all operations constexpr. Even with heap allocation constexpr may help, it you constexpr the dtor and your version of C++ supports it. (Of course such allocations gotta be deleted at compile time too). constexpering all the things is awesome. Embrace it, as I have!
1
u/New-Step3500 1d ago edited 1d ago
I made a video on the constexpr . I delve what it is and when/how to properly use the keyword Constexpr c++
-1
u/zerhud 1d ago
Cpp believes you should write a constexpr keywords everywhere for some reason. May be it likes the keyword more others.
4
1
u/crowbarous 1d ago
Yes, it was weird to adopt
constexpras a keyword that you can use almost always on a (non-opaque) function, and then make you actually write the keyword rather than implying it.
-fimplicit-constexprexists in gcc, but only works on inline functions. Which kind of makes sense, but unfortunately I don't see a corresponding-fimplicit-inlineto truly fix the anti-C++ design of meaninglessly requiring keywords just to allow mechanisms that are already there to do their job.There have been questions about implementing that in Clang, but it doesn't seem to have been met with much enthusiasm. A libc++ maintainer even threatened to break libc++ if this option were added to clang. (WTF...)
2
u/HappyFruitTree 1d ago edited 1d ago
When constexpr functions were introduced in C++11 there were many more limitations. There could basically just be a single return statement, no variables, no loops, no if statements...
I do agree with the concerns of the guy that you linked to. People need to know that the constexpr is intentional if they should be able to rely on it.
12
u/the_poope 1d ago
Yes, first you need to understand why it may give performance improvements. Are you 100% sure you actually understand what
constexprdoes? If you know that, then you should know where you can use it for performance gains.