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

Добавлено 25 сентября 2023 в 08:43

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

Daily bit(e) C++

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

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

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

#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);
  
{ // копирующее конструирование
auto end_it = std::uninitialized_copy(src.begin(), src.end(), raw_it);
// subrange(raw_it,end_it) == {"Hello", "World!"}

// Ручное создание требует ручного уничтожения
std::destroy(raw_it, end_it);
}  

{ // копирующее конструирование из одиночного значения
auto end_it = raw_it + src.size();
std::uninitialized_fill(raw_it, end_it, std::string("Something"));
// subrange(raw_it,end_it) == {"Something", "Something"}

// Ручное создание требует ручного уничтожения
std::destroy(raw_it, end_it);
}

{ // (C++17) перемещающее конструирование
auto end_it = raw_it + src.size();
std::uninitialized_move(src.begin(), src.end(), raw_it);
// subrange(raw_it,end_it) == {"Hello", "World!"}
// src == {"", ""}

// Ручное создание требует ручного уничтожения
std::destroy(raw_it, end_it);
}

// Освобождение буфера
std::free(buffer);
}


{ // (C++20) конструирование значением и по умолчанию
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;

// Конструирование значением
// (для типов POD это означает нулевую инициализацию)
std::uninitialized_value_construct(raw_it, end_it);
// subrange(raw_it, end_it) == {0, 0, 0, 0, 0, 0, 0}

// Для следующего примера
*raw_it = 42;

// Ручное создание требует ручного уничтожения
std::destroy(raw_it, end_it);

// Конструирование по умолчанию
// (для типов POD это означает нулевую инициализацию)
std::uninitialized_default_construct(raw_it, end_it);
// Технически содержимое представляет собой неопределенные значения.
// На практике данные обычно не трогаются.
// subrange(raw_it, end_it) == {42, 0, 0, 0, 0, 0, 0}
// Если вы хотите положиться на это поведение,
// обратитесь к информации от разработчика компилятора.

// Ручное создание требует ручного уничтожения
std::destroy(raw_it, end_it);

// Освобождение буфера
std::free(buffer);
}

Открыть пример на Compiler Explorer.

Теги

C++ / CppDaily bit(e) C++STL / Standard Template Library / Стандартная библиотека шаблоновПрограммирование

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.