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

Can you declare static local variables in a constexpr function?

提问人:Jan Schultke 提问时间:6/25/2023 更新时间:6/25/2023 访问量:163

问:

是否可以在函数中使用局部变量?例如:staticconstexpr

#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);

GCC 12 无法编译此内容,并出现以下错误:

<source>:9:39: error: 'names' defined 'static' in 'constexpr' context
    9 |     static constexpr std::string_view names[] {
      |                                       ^~~~~

其他编译器允许这样做。规则是什么,什么时候允许?

  • 我们可以在一般情况下使用吗,或者static
  • 只是 ,或者static const
  • 只?static constexpr
C++ 常量 constexpr c++23

评论


答:

13赞 Jan Schultke 6/25/2023 #1

自 C++23 以来,此代码是可以的,因为对 的限制已经解除。static constexpr

自 C++23 以来放宽限制

在 C++23 之前,不仅在函数中初始化局部是非法的,而且声明一个也是非法的,即使控制没有流经它。 例如:staticconstexpr

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

禁止变量一直是一个任意的限制,P2647 - 允许在 constexpr 函数中使用静态 constexpr 变量static constexpr

编译器支持

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

C++23 功能 海湾合作委员会 MSVC的
允许在函数中使用变量static constexprconstexpr 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; }

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

大约static const

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

constexpr int remember_first_call(int x) {
    static const int y = x;
    return y;
}

此函数将始终返回首次调用它的参数,该参数在编译时引入了不应存在的“状态”和“时间”概念。 但是,该规则有一个例外: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;
}