可以在 constexpr 函数中声明静态局部变量吗?

Jan*_*tke 8 c++ constants constexpr c++23

可以在函数中使用static局部变量吗?constexpr例如:

#include <string_view>
#include <utility>

enum class axis {
    x, y, z
};

constexpr std::string_view axis_name(axis a) {
    // use static constexpr to avoid putting the table onto the stack
    static constexpr std::string_view names[] {
        "x", "y", "z"
    };
    return names[std::to_underlying(a)];
}

constexpr auto x_name = axis_name(axis::x);
Run Code Online (Sandbox Code Playgroud)

GCC 12 无法编译此错误:

<source>:9:39: error: 'names' defined 'static' in 'constexpr' context
    9 |     static constexpr std::string_view names[] {
      |                                       ^~~~~
Run Code Online (Sandbox Code Playgroud)

其他编译器允许它。规则是什么?什么时候允许?

  • 我们可以static一般使用,或者
  • 只是static const,或者
  • 只是static constexpr

Jan*_*tke 13

这段代码从 C++23 开始就可以了,因为已经取消了限制static constexpr

自 C++23 起放宽限制

static在 C++23 之前,在函数中初始化局部变量不仅是非法的constexpr,而且声明局部变量也是非法的,即使控制不流经它也是如此。例如:

constexpr void foo() {
    if (false) {
        // allowed since C++23, otherwise ill-formed
        // ok because control never flows through x
        static int x;
    }
    // allowed since C++23, otherwise ill-formed
    // ok because static constexpr
    static constexpr int y = 0;
}
Run Code Online (Sandbox Code Playgroud)

禁止static constexpr变量一直是一种任意限制,由P2647 - 允许函数static constexpr中的变量constexpr解除。

编译器支持

要使用此功能,您必须使用最新的编译器。截至撰写本文时,这是编译器支持:

C++23 特性 海湾合作委员会 MSVC
允许函数static constexpr中存在变量constexpr P2647R1 13 16 /

另请参阅:cppreference 上的 C++23 编译器支持页面

static为什么一般不允许?

目前尚不清楚static对象在编译时应如何表现,以及如何在所有编译器中一致地实现这一点。例如,考虑:

constexpr std::size_t get_size() {
    static std::size_t s = 0;
    // different result each time get_size() is called
    return ++s;
}

// what are A and B??? B = 1, and B = 2 are both reasonable
template <std::size_t A = get_size(), B = get_size()>
constexpr std::size_t foo() { return A + B; }
Run Code Online (Sandbox Code Playgroud)

很容易看出,static在编译时使用时会引入大量问题和问题,因此它可能永远不会不受限制。constexpr它打破了函数是函数(没有副作用)的假设,并且也使得记忆它们变得不安全(缓存它们的结果以减少调用它们)。

关于static const

static const也是有问题的,因为它可以初始化为函数参数:

constexpr int remember_first_call(int x) {
    static const int y = x;
    return y;
}
Run Code Online (Sandbox Code Playgroud)

该函数将始终返回第一次调用它时使用的参数,这在编译时引入了不应该存在的“状态”和“时间”的概念。然而,该规则有一个例外static const

constexpr int foo() {
    // allowed since C++23
    // ok because const integers are implicitly constexpr when possible
    static const int x = 3;
    return x;
}
Run Code Online (Sandbox Code Playgroud)