Better_Software_Header_MobileBetter_Software_Header_Web

Find what you need - explore our website and developer resources

On QVarLengthArray and Uninitialized Storage in C++

template <typename T, qsizetype Prealloc = 256>
class QVarLengthArray
{
  // Points to either the internal storage (if capacity <= Prealloc),
  // or to a heap-allocated array otherwise. Since it points to the first
  // element, it's begin().
  T *ptr; 

  // Current size and capacity
  qsizetype size;
  qsizetype capacity;

  // The internal buffer
  alignas(T) std::byte internal_storage[Prealloc * sizeof(T)];
};
void doWork() {
  // number of elements is usually "small"
  QVector<Element> v;
    v.reserve(...);

  while (...) {
    // gather the elements to process
    v.push_back(element);
  }

  for (const Element &e : v) {
    // process them
    process(e);
  }

  // elements are contiguous
  some_c_api(v.data(), v.size());
}
void doWork() {
  // Estimate: max 16 elements "most of the time"
  QVarLengthArray<Element, 16> v;
  v.reserve(...);

  while (...) {
    // gather the elements to process
    v.push_back(element);
  }

  for (const Element &e : v) {
    // process them
    process(e);
  }

  // elements are contiguous
  some_c_api(v.data(), v.size());
}
QVector<int> vec;
vec.resize(10);   // all zeros -- guaranteed

QVarLengthArray<int> qvla;
qvla.resize(10);  // indeterminate values!
auto readDataFromSocket() {
  std::vector<std::byte> buffer;
  buffer.resize(socket->availableBytes());    // resizes AND initializes
  socket->read(buffer.data(), buffer.size()); // overwrites the contents.
  return buffer;
}
std::unique_ptr<int[]> p1 = std::make_unique<int[]>(100'000);
// std::make_unique<int[]>(100'000) returns this:

return std::unique_ptr<int[]>(new int[100'000]())
                                           // ^^ look here
std::unique_ptr<int[]> p2(new int[100'000]); // no ()
// C++20: allocates 100k integers, but does not initialize them
std::unique_ptr<int[]> p3 = std::make_unique_for_overwrite<int[]>(100'000);
// C++23
std::string s = ~~~;

auto oldSize = s.size();

s.resize_and_overwrite(100'000, [oldSize](char *buf, std::size_t count) {

  // For starters, s will *reserve* enough space, without initializing it.
  //
  // - buf points to the string's storage (i.e. s.data()) *after* the reserve;
  // - count is the 1st argument to resize_and_overwrite (100k), so
  //   we can re-use this function with different `count`s.

  // Populate the range [buf, buf+count]. We can mutate the entirety of
  // the string's buffer. But let's say we're just interested in populating
  // the new contents -- from position oldSize up to count.
  for (size_it i = oldSize; i < count; ++i)
    buf[i] = generateData(i);

  // Notes:
  // - If we're growing, the newly created storage is *uninitialized*.
  //   Don't read from it!
  //
  // - The old contents are still there, and we can access them freely.
  //   If needed, carry `oldSize` manually, to identify where to start
  //   writing (and leave the old contents alone).
  //
  // - It is legal to write into buf[count],
  //   but it will be overwritten with \0 when we're done.

  // We don't need to populate the *entire* buffer -- we may stop short!
  // The returned value will be the new size of the string.

  return actual_new_size;
});
QString s(100'000, Qt::Uninitialized);
QByteArray ba(100'000, Qt::Uninitialized);
std::vector<int>     c1(size);  // initializes
std::deque<int>      c2(size);  // initializes
std::list<int>       c3(size);  // initializes
QVector<int>         c4(size);  // initializes
QVarLengthArray<int> c5(size);  // DOES NOT INITIALIZE

cont.resize(newSize);  // initializes the new elements... UNLESS IT'S A QVLA
template <typename Container, typename T>
void fill(Container &container, const T &amp;t)
{
  for (auto &e : container)
    e = t;
}

std::vector<int> v;
fill(v, 42);  // OK

std::vector<bool> v2;
fill(v2, true); // ERROR! Cannot create a (language) reference into a vector<bool>
template <typename <template ...> Storage, typename T> // let users choose the containers they want
auto countFrequencies(T &&collection)
{
  Storage<size_t> c(domain_size);  // for char, that's 256

  for (const auto &e : collection)
    c[e]++;

  return c;
}

About KDAB


GiuseppeD'Angelo

Giuseppe D’Angelo

Senior Software Engineer

Learn Modern C++

Learn more