0

std::vector<unsigned char> or std::vector<char> are the obvious candidates (depending on the context), and indeed, most example code uses them:

https://stackoverflow.com/a/18816228/4442671

However, since std::vector must construct all objects, you end up doing an extraneous memset(), when compared to a simple dynamic allocation: https://godbolt.org/g/TKkwmp

It's not exactly a big deal, but it remains extra O(N) work that gets performed for no reason, so it feels like a break from the "don't pay for it if you don't need it" principle.

Writing a complete RAII wrapper around new[] is not hard, but I can't help but wonder:

Is there a way to get a dynamic container of contiguous uninitialized memory out of the STL, or am I stuck reinventing that wheel?

Edit: I'm really confused by @Someprogrammerdude's reserve() suggestion getting so many votes. Can someone please explain how dereferencing the reserved memory of a 0-sized vector is not a terrible idea?

Frank
  • 9,445
  • 18
  • 34
  • Whats wrong with `std::unique_ptr` ? – Slava Jun 29 '18 at 15:14
  • @slava it doesn't carry the size along, so I would still need a wrapper type. – Frank Jun 29 '18 at 15:14
  • @Someprogrammerdude "The overload (3) zeroes out elements of non-class types such as int, which is different from the behavior of new[], which leaves them uninitialized. To match the behavior of new[], a custom Allocator::construct can be provided which leaves such elements uninitialized." from here https://en.cppreference.com/w/cpp/container/vector/vector – Slava Jun 29 '18 at 15:17
  • 3
    Create the vector as empty (default construction), then [`reserve`](https://en.cppreference.com/w/cpp/container/vector/reserve) the number of elements needed. No initialization will be made. – Some programmer dude Jun 29 '18 at 15:19
  • @Someprogrammerdude `reserve()` only sets the capacity, not the size I would need to `resize()`, which would then perform the construction. – Frank Jun 29 '18 at 15:20
  • 3
    Looks like this is the answer: http://stackoverflow.com/a/21028912/273767 – Slava Jun 29 '18 at 15:20
  • @slava, yes, this is exactly what I needed, thanks! – Frank Jun 29 '18 at 15:20
  • There are still `struct wrapper {char c;};` to have uninitialized struct with char.... – Jarod42 Jun 29 '18 at 15:23

1 Answers1

2

std::get_temporary_buffer does exactly that:

Allocates uninitialized contiguous storage, which should be sufficient to store up to count adjacent objects of type T. The request is non-binding and the implementation may allocate less or more than necessary to store count adjacent objects.

You could pair it up with std::unique_ptr to provide a custom destructor since such buffers need to be freed with std::return_temporary_buffer().

The sad thing about it is it was deprecated in C++17 and removed in C++20 :(

You can ever build object in this temporary buffer with an output iterator: std::raw_storage_iterator:

#include <iostream>
#include <string>
#include <memory>
#include <algorithm>

int main()
{
    const std::string s[] = {"This", "is", "a", "test", "."};
    std::string* p = std::get_temporary_buffer<std::string>(5).first;

    std::copy(std::begin(s), std::end(s),
              std::raw_storage_iterator<std::string*, std::string>(p));

    for(std::string* i = p; i!=p+5; ++i) {
        std::cout << *i << '\n';
        i->~basic_string<char>();
    }
    std::return_temporary_buffer(p);
}
YSC
  • 34,418
  • 7
  • 80
  • 129