使用 constexpr 到 return 指针

using constexpr to return pointer

从 C++20 开始,我们可以在编译时分配内存,并且必须在编译时释放它。因此,这给我提出了一些问题: 首先为什么这样做?

constexpr int* return_ptr() {
    return new int{ 1 };
}

void call_return_ptr(){
    int* int_ptr = return_ptr();
}

int main() {
    call_return_ptr();
    return 0;
}

我正在使用从 constexpr 返回的 ptr,而不是 在编译期间释放它。根据我的理解,这应该是一个错误。 其次,如果第一个示例有效,那么为什么这不起作用:

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;
}

即使我们尝试像这样在编译时删除指针:

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;
}

这仍然是一个错误。这是怎么回事?

---编辑 Nicol Bolas 对我问题的第一部分和第二部分给出了非常好的和深刻的答案,但这仍然是第三个版本(call_return_ptr 也是 constexpr 的那个)不起作用的原因。根据对 Nicol Bolas 回答的评论,我仍然不清楚,为什么即使我们将第三个版本的函数签名更改为 consteval 仍然会出错,因为 call_return_ptr 中的所有内容都应该在constexpr 上下文。

没有“编译时”这样的东西。不是真的。

有什么是“常量表达式求值”。这些是特定类别的表达式,其评估以特定方式发生,因此其评估结果可用于源代码。

constexpr 函数是 可以在常量表达式求值期间 求值的函数。如果您在常量表达式上下文之外调用它,则它不会进行常量表达式求值。

constexpr(或constinit)变量的初始值设定项是常量表达式上下文。因此,它必须经过常量表达式求值。

常量表达式内存分配规则仅适用于在常量表达式上下文中调用函数时。因此,您的第一个 call_return_ptr 调用 return_ptr 并将结果存储在运行时变量中。这不是常量表达式上下文;这只是正则表达式评估。该函数分配内存和 returns 一个指针,就像其他函数一样。

您的第二个版本使用调用 return_ptr 的结果来初始化 constexpr 变量。这个 一个常量表达式上下文...直到变量完成初始化。看,您在常量表达式上下文之外调用了 call_return_ptr,因此该函数的唯一部分是常量表达式上下文是 constexpr 变量的初始化。其他一切,包括 delete 调用,都不是常量表达式上下文的一部分。

因此,除非您在常量表达式上下文中调用此函数,否则您将收到编译错误,因为常量计算会产生一个指向 constexpr 分配的内存的指针,该内存在该常量计算中未被删除。

如果要确保特定函数总是常量表达式上下文,则必须将其声明为consteval

第三个不起作用或多或少出于相同的原因:该函数未在常量表达式上下文中调用。事实上,void 函数本身不能在常量表达式上下文中,除非它是 consteval,因为没有 void 函数的结果可用于常量计算。

此外,通过生成内部变量 constexpr,您在函数 中创建了常量表达式上下文 。问题是,不断评估的规则是递归的。常量表达式上下文不得泄漏内存,即使 该上下文在 另一个常量表达式上下文中计算。每个常量表达式上下文都必须遵循这些规则,无论其评估之外的上下文如何。