为什么 `std::deque` 不支持 `constexpr`?

7
我正在学习c++ STL,注意到std::vectorstd::array(连续存储)支持的大多数功能都被标记为constexpr,但std::deque和其他非连续存储则不是这种情况。因此我花了一些时间做研究,发现了一个2019年的提议Making std::deque constexpr,但std::deque仍未实现其方法的constexpr
我的困惑在于std::array保证其元素存储在堆栈上;就像普通C风格数组一样,因此它应该在编译时计算,但std::vector在堆上分配内存,所以如果它在编译时评估,那么deque也是如此,对吗?
谢谢!

答案其实是:这比它值得的工作还要多。 - Hunter Kohler
1个回答

5
根据https://github.com/cplusplus/papers/issues/665 ,该提案的进展情况有记录,似乎存在一些疑虑,即无法在核心语言不变的情况下实现constexprstd::deque。不幸的是,它没有说明具体的问题所在。可能是某些常见实现使用了某些语言结构,这些结构在常量表达式中是不允许的,或者实现依赖于某些标准规定的未定义行为结构。后者通常对于标准库来说不是问题,因为它不受语言规则的约束,并且可以对特定编译器的行为进行假设。然而,在常量表达式的核心语言未定义行为始终是一个严重的错误,因此这样的结构在常量表达式上下文中经常不能使用,除非引入魔术编译器解决方法。正如链接的github问题中提到的那样,似乎还需要添加constexpr以使其正常工作的某些库设施。
除了这些问题,一般来说,我认为没有理由不使所有容器和容器适配器都友好于constexpr,因为现在可以在常量表达式中使用std::allocator。他们可能只是想确保它们可以正确地实现为constexpr。我猜想出于同样的原因,只有std::stringstd::vector被用于C++20,因为这些是最简单和最重要的分配器感知容器。(std::array已经很长时间以来是constexpr,因为它不需要任何动态分配。)
虽然看到问题的最后一个条目的日期(以及与之相关的std::liststd::priority_queue等问题),似乎在过去两年里没有进展,也许是因为提案的作者没有进一步追求,但我无法确定。
无论哪种情况,当我们说std::vector(或其他分配器感知容器)是constexpr友好的时,这意味着与例如std::array不同。您可以声明一个constexpr std::array变量并像使用const std::array变量一样使用它,但是您不能声明constexpr std::vector变量(通常根本不行),然后像使用const std::vector一样使用它。

然而,您可以在例如constexpr函数中使用std::vector变量进行一些计算,只要该变量是在常量表达式评估开始之后创建并在结束之前销毁即可。目前这对于例如std::list来说是不可能的,因为它不是constexpr友好的。

原因在于编译器实际上不会在编译时为容器分配内存,然后以某种方式将其转移到运行时分配(静态或动态)。相反,在编译时的动态分配与运行时的分配是分开的,并且必须在分配它们的常量表达式评估结束之前被释放。


非常感谢您提供的详细信息。“我认为没有理由不使所有容器和容器适配器都支持constexpr”——这是我对STL产生疑问的主要原因。正如我上面提到的,我知道std::array是常量表达式友好的,因为它在编译时计算。我还查看了vector和deque的源代码,似乎它们都使用std::allocator来处理内存管理。所以我想知道为什么vector中的大多数方法是constexpr的,而deque中的方法却不是。 - Richard H. Nguyen
@Yuuta 自 C++20 开始,通过 std::allocator 进行的动态分配才能被视为 constexpr 友好。随着这一变化的出现,使用它来创建容器也成为了 constexpr 友好的可能。至少要使一个类成为 constexpr 友好的,您需要在大多数或所有成员函数上放置 constexpr,这将是管理语言的 C++ 标准中的主要更改。但仅在函数上放置 constexpr 并不总是足够使其正常工作。 - user17732522
对于一个简单的例子,reinterpret_cast 在常量表达式中是被禁止的,因此如果类应该是 constexpr 友好的,则不能在成员函数定义中使用它。在标准委员会添加 constexpr 之前,必须确信标准库实现者可以使其当前的容器实现允许 constexpr 而不会出现重大问题。确保这一点的过程需要一些时间和工作,并且似乎已经在 std::deque 中停滞不前。从我在链接中看到的内容来看,最初看起来它可能会在 C++23 中实现。 - user17732522
@Yuuta,还要看一下链接中的投票,例如“我们应该承诺更多委员会时间来追求[这些提案],知道我们的时间很少,这将减少其他工作的时间。”有一些反对票。这表明,优先考虑哪些语言特性值得花费时间进行开发也是一个问题。其他更重要的库类型也没有成为constexpr友好型,并需要委员会时间,例如std::unique_ptr,最近已经为C++23草案做出了constexpr友好型(https://github.com/cplusplus/papers/issues/961)。 - user17732522

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接