Daily bit(e) C++ #230. Неинициализированные алгоритмы, которые могут создавать и уничтожать объекты в необработанных блоках памяти.

Ручное управление сроком службы и создание объектов внутри нетипизированных блоков памяти — очень нишевая тема.

Однако бывают ситуации, когда std::vector недостаточно.

К счастью, стандартная библиотека C++ предлагает набор неинициализированных алгоритмов, которые обеспечивают по умолчанию, копирование, перемещение и конструирование и уничтожение значений поверх необработанной памяти.

#include <vector>
#include <string>
#include <memory>

std::vector<std::string> src{"Hello", "World!"};

{ 
void* buffer = std::aligned_alloc(alignof(std::string),
                                  sizeof(std::string) * src.size());
if (buffer == nullptr) std::abort();
auto raw_it = static_cast<std::string*>(buffer);
  
{ // Copy construction
auto end_it = std::uninitialized_copy(src.begin(), src.end(), raw_it);
// subrange(raw_it,end_it) == {"Hello", "World!"}

// Manual creation requires manual destruction
std::destroy(raw_it, end_it);
}  

{ // Copy construction from a single value
auto end_it = raw_it + src.size();
std::uninitialized_fill(raw_it, end_it, std::string("Something"));
// subrange(raw_it,end_it) == {"Something", "Something"}

// Manual creation requires manual destruction
std::destroy(raw_it, end_it);
}

{ // (C++17) Move construction
auto end_it = raw_it + src.size();
std::uninitialized_move(src.begin(), src.end(), raw_it);
// subrange(raw_it,end_it) == {"Hello", "World!"}
// src == {"", ""}

// Manual creation requires manual destruction
std::destroy(raw_it, end_it);
}

// Free the buffer
std::free(buffer);
}


{ // (C++20) Value and Default construction
constexpr size_t size = 7;
void* buffer = std::aligned_alloc(alignof(int), 
                                  sizeof(int) * size);
if (buffer == nullptr) std::abort();

auto raw_it = static_cast<int*>(buffer);
auto end_it = raw_it + size;

// Value construction (for POD types this means zero-initialization)
std::uninitialized_value_construct(raw_it, end_it);
// subrange(raw_it, end_it) == {0, 0, 0, 0, 0, 0, 0}

// For the next example
*raw_it = 42;

// Manual creation requires manual destruction
std::destroy(raw_it, end_it);

// Default construction (for POD types this means no initialization)
std::uninitialized_default_construct(raw_it, end_it);
// Technically, the content is indeterminate values.
// In practical terms the data will typically not be touched.
// subrange(raw_it, end_it) == {42, 0, 0, 0, 0, 0, 0}
// If you want to rely on this, check with your compiler vendor.

// Manual creation requires manual destruction
std::destroy(raw_it, end_it);

// Free the buffer
std::free(buffer);
}

Откройте пример в Compiler Explorer.