ClassY
ClassY

Reputation: 655

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.

Upvotes: 14

Views: 797

Answers (1)

Nicol Bolas
Nicol Bolas

Reputation: 474546

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.

Upvotes: 17

Related Questions