Daily bit(e) of C++ | std::uninitialized_copy, std::uninitialized_fill, std::uninitialized_move, std::uninitialized_value_construct, std::uninitialized_default_construct, std::destroy
2 min readAug 19, 2023
Daily bit(e) of C++ #230, The uninitialized algorithms that can create and destroy objects in raw memory blocks.
Manual lifetime management and creating objects inside untyped memory blocks is a very niche topic.
However, there are situations when std::vector isn’t sufficient.
Fortunately, the C++ standard library offers a set of uninitialized algorithms that provide default, copy, move and value construction and destruction on top of raw memory.
#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);
}