Daily bit(e) of C++ | std::uninitialized_copy, std::uninitialized_fill, std::uninitialized_move, std::uninitialized_value_construct, std::uninitialized_default_construct, std::destroy

Šimon Tóth
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);
}

Open the example in Compiler Explorer.

--

--

Šimon Tóth

I'm an ex-Software Engineer and ex-Researcher focusing on providing free educational content.