使用 constexpr 返回指针

Cla*_*ssY 13 c++ constexpr c++20

从 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;
}
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

即使我们在编译时尝试删除指针,如下所示:

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;
}
Run Code Online (Sandbox Code Playgroud)

这仍然是一个错误。这里发生了什么?

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

Nic*_*las 17

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

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

constexpr功能是一个功能可以被常量表达式评估期间评估。如果在常量表达式上下文之外调用它,则它不会进行常量表达式求值。

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

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

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

所以除非你在常量表达式上下文中调用这个函数,否则你会得到一个编译错误,因为常量评估产生了一个指向 constexpr 分配的内存的指针,在该常量评估中没有删除。

如果要确保特定函数始终是常量表达式上下文,则必须对其进行声明consteval

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

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