【问题标题】:using constexpr to return pointer使用 constexpr 返回指针
【发布时间】:2021-10-17 13:05:10
【问题描述】:

从 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 回答的 cmets 上的内容,我仍然不清楚,为什么即使我们将第三版的函数签名更改为 consteval 仍然会出现错误,因为 call_return_ptr 中的所有内容都应该在 constexpr 上下文中.

【问题讨论】:

  • constexpr 函数既可以在编译时调用,也可以在运行时调用,因此第一个示例有效的事实并不意味着第二个示例应该有效。

标签: c++ c++20 constexpr


【解决方案1】:

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

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

constexpr 函数是一个可能在常量表达式求值过程中求值的函数。如果您在常量表达式上下文之外调用它,则它不会进行常量表达式求值。

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

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

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

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

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

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

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

【讨论】:

  • 感谢您的宝贵时间和深刻的回答。我已经阅读了您的答案,但我无法弄清楚为什么第三个版本是错误的。因为根据您所说的,它在 constexpr 上下文中被调用并且我正在释放内存,那么为什么会出现错误?不过,我完全理解第一个和第二个版本的问题。
  • @ClassY: "因为根据你所说的,它是在 constexpr 上下文中调用的" 不,不是。调用函数的表达式不使用返回值来初始化constexpr 变量。另外,它是一个void 函数,所以返回值不能初始化一个constexpr 变量。所以函数调用本身并不是一个常量表达式上下文;只有函数内部constexpr变量的初始化是常量表达式上下文。
  • 但是为什么编译器决定第三个版本不需要是 constexpr 并删除它呢?而且我还通过将函数签名从 constexpr 更改为 consteval 再次尝试了第三个版本,但仍然失败!
  • @ClassY:你留下变量constexpr了吗?如果是这样……为什么?
  • 因为我不会更改指针指向的位置,并且由于它已经在 constexpr 上下文中,所以它的唯一含义是应用 const。推理除了我要提出的观点之外,您能否帮我解释一下为什么会出现错误(第三个版本,我们在 cmets 中谈到了更改)?
猜你喜欢
  • 2012-05-09
  • 1970-01-01
  • 1970-01-01
  • 2012-11-10
  • 2019-10-06
  • 2020-02-08
  • 1970-01-01
相关资源
最近更新 更多