25

According to the reference a simple std::vector<T> vec; creates an emtpy container (default constructor). Does this guarantee that there is no dynamic memory allocation? Or may an implementation chose to reserve some memory?

I known that, for this empty constructor, there is no construction of the type T since C++11. However, I wonder, if there is also a guarantee that nothing is allocated on heap. I.e. that the above line is just a few nullptr on stack/member.

I tested it with vc140, where it is indeed free of dynamic allocations.

Johannes Jendersie
  • 980
  • 10
  • 16
  • 1
    Since the constructor is `noexcept`, doesn't that guarantee it (otherwise it could throw `std::bad_alloc`)? – Quentin Feb 12 '18 at 10:42
  • 4
    @Quentin the constructor can catch such exception and carry on. – eerorika Feb 12 '18 at 10:45
  • 1
    Looks like a duplication of this: https://stackoverflow.com/questions/8036474/when-vectors-are-allocated-do-they-use-memory-on-the-heap-or-the-stack – laker93 Feb 12 '18 at 10:47
  • @Quentin just because the constructor is noexcept doesn't mean that it couldn't *try* to opportunistically allocate some memory, catch the bad_alloc and just refrain from allocating anything if `new` throws. noexcept just means the constructor won't throw itself - it doesn't prevent it from calling stuff that throws as long as it handles the exception. – Jesper Juhl Feb 12 '18 at 10:47
  • Fair enough. -- – Quentin Feb 12 '18 at 10:48
  • 4
    @LukePurnell Its not a duplicate. Your linked question is about where elements and header information go. My question is about WHEN memory for the elements is allocated. – Johannes Jendersie Feb 12 '18 at 10:52
  • Why do you want to know? – Martin Bonner supports Monica Feb 12 '18 at 12:04
  • @MartinBonner a dynamic allocation is expensive and if there are tons of objects creating empty vectors in their constructors for example in a `vector> x` it might hurt. Especially, if many of those will stay empty their entire lifetime. – Johannes Jendersie Feb 12 '18 at 12:40
  • Right. So it is a performance issue, rather than correctness issue. The standard tends to leave things like that to "quality if implementation". The only exception is big-O stuff, but this isn't big-O: the time taken to construct vectors will still be O(number-of-vectors) - this is just about the constant. (Admittedly, the constant may change by a factor of 1000, but ...) – Martin Bonner supports Monica Feb 12 '18 at 13:18
  • 1
    Also related [Is the compiler allowed to optimize out heap memory allocations?](https://stackoverflow.com/q/31873616/1708801) – Shafik Yaghmour Feb 16 '18 at 22:06
  • @MartinBonner also notable, it helps creating containers that can be instantiated on incomplete types – sehe Feb 16 '18 at 22:48
  • 1
    On MSVC (2019) in Debug, there will be an allocation, but not in Release. – scx Oct 14 '19 at 17:37

3 Answers3

29

Does this guarantee that there is no dynamic memory allocation?

No. It is however quite typical that an implementation doesn't allocate memory. I haven't seen a standard library implementation that does.

Or may an implementation chose to reserve some memory?

It may, but that's atypical.

I known that, for this empty constructor, there is no construction of the type T since C++11

Also prior to C++11.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 6
    The standard offers no guarantees of course, but it is quite likely that the default constructor will not allocate - doing an allocation would pessimize the "default construct + reserve" construct (it will either have to do *two* allocations, or will end up allocating too much memory). – Martin Bonner supports Monica Feb 12 '18 at 12:06
  • 2
    I think it's important to mention that msvc will, in debug, allocate on the default constructor of `std::vector`. It does so as well for `std::string` and a few other containers. This happens when `_ITERATOR_DEBUG_LEVEL` is set to non-0 (1 or 2) and it enables several runtime checks. The default value of `_ITERATOR_DEBUG_LEVEL` is 2 in debug and 0 in release. You can read more here: https://learn.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=vs-2019 – GaspardP Nov 02 '19 at 01:45
29

std library is part of the C++ language.

Almost any call to any std library class or function could do pathological and insane things. But the same is true of int x=7; -- the standard is not written to defend against frankly hostile C++ implementations, which includes the std library.

That being said, the zero argument constructor to std vector is noexcept. This means it is intended to not allocate. A hostile implementation is free to allocate, catch any errors, and proceed regardless of if the allocation succeeded. A hostile implementation is also free to count to 47 trillion, run some FFT on random data, spin up a neural network and train it against Shakespeare, compose some sonnets, then proceed as if nothing happened. The standard has nothing to say on the inobservable poetry composition of any operation in C++; so long as the action has no observable (within the abstract machine) side effects, the standard has no opinion.

In practice there is no reason for std::vector<T>() to allocate, and no later operation on it can assume it allocated. I could see an instrumented build allocating some lifetime tracking token to enforce iterator invalidation errors, but that would only be enabled in debug with extra flags (e.g. -DCMP_JUN17).

Worry more about poetry than a call to new.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
3

There is no guarantee.

A counter example that I just hit (and which led me to this post) is MSVC2017's STL implementation, if _ITERATOR_DEBUG_LEVEL > 0.