您可以在编译时将 std::vector 转换为 std::array 而不使向量两次吗?

fda*_*dan 19 c++ constexpr c++20

我正在编译时使用计算一些数据std::vector,并希望将结果作为数组返回,以便可以在运行时进一步使用。我在不进行两次计算的情况下设置数组大小时遇到​​问题。

这是我迄今为止所做的事情的简化示例。代码按预期编译并运行。

constexpr auto make_vector() { 
  // complex calculation here
  return std::vector{1, 2, 3}; 
}

constexpr auto make_array() {
  const auto vec = make_vector();
  std::array<int, make_vector().size()> result{};

  std::copy(vec.cbegin(), vec.cend(), result.begin());
  return result;
}

int main() {
  constexpr auto result = make_array(); // std::array<int, 3>{1, 2, 3}
}
Run Code Online (Sandbox Code Playgroud)

我理解为什么不能用于vec.size()数组大小以及为什么make_vector().size()会产生编译时常量。做两次似乎不是正确的方法。

有没有办法避免调用make_vector两次?我在这里错过了一个基本概念吗?

Jan*_*tke 19

有没有办法避免调用make_vector两次?我在这里错过了一个基本概念吗?

目前,没有。根本问题是它std::vector不是文字类型,因此您不能简单地将其存储在constexpr可以任意使用其大小和内容的变量中。Astd::vector只能在常量表达式(1)期间暂时存在。

在第一个常量表达式中,.size()不是常量表达式,并且无法“将其提升到该状态”。因此,您不知道要创建的数组的大小,并且第一次会浪费向量内容(2)

这个问题也是P0784:更多 constexpr 容器(3)中的动机:

除此之外,缺乏可变大小的 [constexpr] 容器迫使它们在实现中使用原始的固定大小的数据结构,并解析输入的 JSON 字符串两次;一次确定数据结构的大小,一次将 JSON 解析为这些结构。

您不是第一个遇到这个问题的人,您只需要有耐心,直到 C++ 委员会和编译器开发人员解决这个问题。

解决此问题的最新论文是P1974:非瞬态 constexpr 分配。关键问题是任何T*在编译时分配并存储在constexpr变量中(直接或通过容器)的内容都可以指向可变内存。这个常量问题意味着 aconstexpr std::vector<std::string>将包含可变/不可变指针的混合(类似于char ** const),这打破了我们可以delete完全省略并保留静态内存的假设。请参阅论文了解更多详细信息。


(1)原因是即使std::vector可以在常量表达式中使用new,所有的内存也必须在常量表达式结束之前被释放。这称为“临时分配”。

(2)正如评论者 @HolyBlackCat 所指出的,您的编译器可能会记住对的调用make_vector(),以便即使您调用它两次,该函数也不需要计算两次。

(3)本文已被C++20接受,但尚未解决容器创建浪费的问题。