44

考虑以下代码段:

#include <array>
int main() {
  using huge_type = std::array<char, 20*1024*1024>;
  huge_type t;
}

显然它会在大多数平台上崩溃,因为默认堆栈大小通常小于 20MB。

现在考虑以下代码:

#include <array>
#include <vector>

int main() {
  using huge_type = std::array<char, 20*1024*1024>;
  std::vector<huge_type> v(1);
}

令人惊讶的是它也崩溃了!回溯(使用最近的 libstdc++ 版本之一)指向include/bits/stl_uninitialized.h文件,我们可以在其中看到以下行:

typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType;
std::fill(__first, __last, _ValueType());

调整大小的vector构造函数必须默认初始化元素,这就是它的实现方式。显然,_ValueType()临时使堆栈崩溃。

问题是它是否是一个符合要求的实现。如果是,这实际上意味着使用巨大类型的向量是非常有限的,不是吗?

4

3 回答 3

19

任何标准 API 使用多少自动存储都没有限制。

它们都可能需要 12 TB 的堆栈空间。

但是,该 API 只需要Cpp17DefaultInsertable,并且您的实现会在构造函数所需的内容之上创建一个额外的实例。除非它在检测到对象是微不足道的可操作和可复制的之后被门控,否则该实现看起来是非法的。

于 2020-01-06T20:51:00.377 回答
9
huge_type t;

显然它会在大多数平台上崩溃......

我对“大多数”的假设提出异议。由于从未使用过巨大对象的内存,因此编译器可以完全忽略它并且永远不会分配内存,在这种情况下不会发生崩溃。

问题是它是否是一个符合要求的实现。

C++ 标准不限制堆栈的使用,甚至不承认堆栈的存在。所以,是的,它符合标准。但人们可以认为这是一个实施质量问题。

这实际上意味着使用巨大类型的向量是非常有限的,不是吗?

libstdc++ 似乎就是这种情况。使用 libc++(使用 clang)没有重现崩溃,所以这似乎不是语言的限制,而只是在那个特定的实现中。

于 2020-01-06T20:58:10.820 回答
5

我不是语言律师,也不是 C++ 标准专家,但 cppreference.com 说:

explicit vector( size_type count, const Allocator& alloc = Allocator() );

使用默认插入的计数 T 实例构造容器。不制作副本。

也许我误解了“默认插入”,但我希望:

std::vector<huge_type> v(1);

相当于

std::vector<huge_type> v;
v.emplace_back();

后一个版本不应该创建堆栈副本,而是直接在向量的动态内存中构造一个 huge_type。

我不能权威地说你看到的是不合规的,但这肯定不是我对高质量实施的期望。

于 2020-01-06T22:05:55.850 回答