从立即函数返回 std::vector

Fed*_*dor 10 c++ stdvector language-lawyer c++20 consteval

从 C++20 开始,std::vector可以在常量表达式中使用。据我所知,当前的 C++ 允许动态分配内存,条件是任何此类分配在常量表达式“结束”时都被释放。

但是,我遇到的情况是,在立即运行的情况下,规则可能会有所不同。请考虑以下示例:

consteval auto getVec() {
    return std::vector<int>(9);
}

static_assert( getVec().size() == 9 );
Run Code Online (Sandbox Code Playgroud)

这里立即consteval函数getVec返回 not-empty std::vector,其大小在常量表达式中验证。

我期望这段代码能够编译,因为所有释放都必须自动完成,并且实际上它在 Clang 中被接受libc++

但 MSVC 抱怨道:

error C7595: 'getVec': call to immediate function is not a constant expression
note: (sub-)object points to memory which was heap allocated during constant evaluation
fatal error C1903: unable to recover from previous error(s); stopping compilation
Run Code Online (Sandbox Code Playgroud)

GCC 的行为类似:

error: 'getVec()()' is not a constant expression because it refers to a result of 'operator new'
Run Code Online (Sandbox Code Playgroud)

在线演示: https: //gcc.godbolt.org/z/d736qr3hh

这里哪个编译器是正确的?

Art*_*yer 10

随着P2564R3被接受为 C++20 的缺陷报告,该问题已得到修复。它允许getVec()成为较大常量表达式的子表达式,而本身不成为常量表达式。

LLVM 17 和即将推出的 GCC 14 实现了它。对于 MSVC 有一个功能请求


问题的最初原因是这样的:

[expr.prim.id]p3

表示立即函数 ([dcl.constexpr]) 的潜在评估id 表达式仅应出现

  • 作为立即调用的子表达式,或者
  • 在立即函数上下文中 ([expr.const])

[expr.const]p13 :

如果表达式或转换可能被计算并且其最内部的非块作用域是立即函数的函数参数作用域,则该表达式或转换位于立即函数上下文中。如果表达式或转换是立即函数的潜在评估显式或隐式调用并且不在立即函数上下文中,则它是立即调用。立即调用应是常量表达式。

static_assert( getVec().size() == 9 );不在直接函数上下文中。因此,显式调用getVec()是立即调用,因此需要生成常量表达式。getVec()它本身不是一个常量表达式,因为它不会释放 new 分配的内存。

为了比较,以下编译https://godbolt.org/z/9KqhY7oP8

consteval auto getVec() {
    return std::vector<int>(9);
}

consteval auto getVecSize() {
    return getVec().size();
}

static_assert(getVecSize() == 9);
static_assert([]() consteval { return getVec().size() == 9; }());

consteval void InAnImmediateFunctionContext() {
    static_assert(getVec().size() == 9);
}
Run Code Online (Sandbox Code Playgroud)