Since C++20 we can allocate memory during compile time and we have to free that during compile time. Therefore this raised some questions for me: first why does this work?
constexpr int* return_ptr() {
return new int{ 1 };
}
void call_return_ptr(){
int* int_ptr = return_ptr();
}
int main() {
call_return_ptr();
return 0;
}
I'm using the ptr returned from constexpr
and not freeing that during compile time. This should be an error based on what I've understood.
Secondly if the first example works then why this shouldn't work:
constexpr int* return_ptr() {
return new int{ 1 };
}
void call_return_ptr(){
constexpr int* int_ptr = return_ptr();
}
int main() {
call_return_ptr();
return 0;
}
and even if we try to delete the pointer during compile time like this:
constexpr int* return_ptr() {
return new int{ 1 };
}
constexpr void call_return_ptr(){
constexpr int* int_ptr = return_ptr();
delete int_ptr;
}
int main() {
call_return_ptr();
return 0;
}
this is still an error. What is going on here?
---EDIT
Nicol Bolas gave a very good and profound answer to first and somewhat second part of my question, but that still leaves why the third version (the one that call_return_ptr
is also constexpr) isn't working. Based what's on the comments on Nicol Bolas's answer, it is still unclear to me, why even if we change the third version's function signature to consteval
that still gives an error since everything in call_return_ptr
should be in a constexpr
context.
C++'s constexpr brings another new dimension to the problem too! It behaves like const in the sense that it makes all pointers constant pointers. But because it occurs at the start of your statement (rather than after the '*') its not immediately obvious.
A constexpr function is a function that can be invoked within a constant expression. A constexpr function must satisfy the following conditions: It is not virtual. Its return type is a literal type.
In Conclusion. constexpr is an effective tool for ensuring compile-time evaluation of function calls, objects and variables. Compile-time evaluation of expressions often leads to more efficient code and enables the compiler to store the result in the system's ROM.
Yes. I believe putting such const ness is always a good practice wherever you can. For example in your class if a given method is not modifying any member then you always tend to put a const keyword in the end.
There is no such thing as "compile-time". Not really.
What there is is "constant expression evaluation". These are a certain category of expressions whose evaluation happens in a certain way, such that the results of their evaluation are available to the source code.
A constexpr
function is a function which may be evaluated during constant expression evaluation. If you call it outside of a constant expression context, then it does not undergo constant expression evaluation.
The initializer for a constexpr
(or constinit
) variable is a constant expression context. Therefore, it must undergo constant expression evaluation.
The rules for constant expression memory allocation only apply when the function is called within a constant expression context. So your first call_return_ptr
calls return_ptr
and stores the result in a runtime variable. This is not a constant expression context; it's just regular expression evaluation. The function allocates memory and returns a pointer, just like any other.
Your second version uses the result of that call to return_ptr
to initialize a constexpr
variable. This is a constant expression context... right up until the variable finishes initialization. See, you called call_return_ptr
outside of a constant expression context, so the only part of that function which is a constant expression context is the initialization of the constexpr
variable. Everything else, including the delete
call, is not part of a constant expression context.
So unless you call this function within a constant expression context, you'll get a compile error since constant evaluation produced a pointer to constexpr allocated memory that was not deleted in that constant evaluation.
If you want to ensure that a particular function is always a constant expression context, it must be declared consteval
.
The third one doesn't work for more or less the same reason: the function is not being called in a constant expression context. Indeed, a void
function cannot itself be in a constant expression context unless it is consteval
, since there is no result of a void
function to use in constant evaluation.
Furthermore, by making the inner variable constexpr
, you have created a constant expression context within the function. The thing is, the rules of constant evaluation are recursive. A constant expression context must not leak memory, even if that context is evaluated within another constant expression context. Each constant expression context has to follow those rules, regardless of the context outside of its evaluation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With