Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using constexpr to return pointer

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.

like image 520
ClassY Avatar asked Aug 14 '21 19:08

ClassY


People also ask

Can a pointer be constexpr?

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.

Can a function return constexpr?

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.

Does constexpr improve performance?

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.

Should I use constexpr everywhere?

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.


1 Answers

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.

like image 164
Nicol Bolas Avatar answered Oct 16 '22 23:10

Nicol Bolas