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); }