std::vector (ab)uses automatic storage

3.1k Views Asked by At

Consider the following snippet:

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

Obviously it would crash on most of platforms, because the default stack size is usually less than 20MB.

Now consider the following code:

#include <array>
#include <vector>

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

Surprisingly it also crashes! The traceback (with one of the recent libstdc++ versions) leads to include/bits/stl_uninitialized.h file, where we can see the following lines:

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

The resizing vector constructor must default-initialize the elements, and this is how it's implemented. Obviously, _ValueType() temporary crashes the stack.

The question is whether it's a conforming implementation. If yes, it actually means that the use of a vector of huge types is quite limited, isn't it?

3

There are 3 best solutions below

5
Yakk - Adam Nevraumont On BEST ANSWER

There is no limit on how much automatic storage any std API uses.

They could all require 12 terabytes of stack space.

However, that API only requires Cpp17DefaultInsertable, and your implementation creates an extra instance over what is required by the constructor. Unless it is gated behind detecting the object is trivially ctorable and copyable, that implementation looks illegal.

5
eerorika On
huge_type t;

Obviously it would crash on most of platforms ...

I dispute the assumption of "most". Since the memory of the huge object is never used, the compiler can completely ignore it and never allocate the memory in which case there would be no crash.

The question is whether it's a conforming implementation.

The C++ standard doesn't limit stack use, or even acknowledge the existence of a stack. So, yes it conforms to the standard. But one could consider this to be a quality of implementation issue.

it actually means that the use of a vector of huge types is quite limited, isn't it?

That appears to be the case with libstdc++. The crash was not reproduced with libc++ (using clang), so it seems that this is not limitation in the language, but rather only in that particular implementation.

6
Adrian McCarthy On

I'm not a language lawyer nor a C++ standard expert, but cppreference.com says:

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

Constructs the container with count default-inserted instances of T. No copies are made.

Perhaps I'm misunderstanding "default-inserted," but I would expect:

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

to be equivalent to

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

The latter version shouldn't create a stack copy but construct a huge_type directly in the vector's dynamic memory.

I can't authoritatively say that what you're seeing is non-compliant, but it's certainly not what I would expect from a quality implementation.