std::ranges::uninitialized_default_construct
| Определено в заголовочном файле <memory>
|
||
| Сигнатура вызова |
||
template< прямой-итератор-без-исключения I, ограничитель-без-исключения-для<I> S > requires std::default_initializable<std::iter_value_t<I>> I uninitialized_default_construct( I first, S last ); |
(1) | (начиная с C++20) |
template< прямой-диапазон-без-исключения R > requires std::default_initializable<ranges::range_value_t<R>> ranges::borrowed_iterator_t<R> uninitialized_default_construct( R&& r ); |
(2) | (начиная с C++20) |
std::iter_value_t<I> в неинициализированном хранилище, определяемом диапазоном [first, last) инициализацией по умолчанию, как если бы
for (; first != last; ++first)
::new (static_cast<void*>(std::addressof(*first)))
std::remove_reference_t<std::iter_reference_t<I>>;
r в качестве диапазона, как если бы использовались ranges::begin(r) в качестве first, а ranges::end(r) в качестве last.Функционально-подобные объекты, описанные на этой странице, являются ниблоидами, то есть:
- Явные списки аргументов шаблона не могут быть указаны при вызове любого из них.
- Ни один из них не виден для поиска, зависящего от аргумента.
- Когда какой-либо из них обнаруживается обычным неквалифицированным поиском по имени слева от оператора вызова функции, поиск, зависящий от аргумента запрещён.
На практике они могут быть реализованы как функциональные объекты или со специальными расширениями компилятора.
Параметры
| first, last | — | пара итератор-ограничитель, обозначающая диапазон элементов для инициализации |
| r | — | диапазон элементов для инициализации |
Возвращаемое значение
Итератор, равный last.
Сложность
Линейная по расстоянию между first и last.
Исключения
Исключение, генерируемые при создании элементов в целевом диапазоне, если таковые имеются.
Примечание
Реализация может пропустить создание объектов (без изменения наблюдаемого эффекта), если при инициализации по умолчанию объекта std::iter_value_t<I> не вызывается нетривиальный конструктор по умолчанию, который может быть обнаружен std::is_trivially_default_constructible_v.
Возможная реализация
struct uninitialized_default_construct_fn
{
template<прямой-итератор-без-исключения I, ограничитель-без-исключения-для<I> S>
requires std::default_initializable<std::iter_value_t<I>>
I operator()(I first, S last) const
{
using ValueType = std::remove_reference_t<std::iter_reference_t<I>>;
if constexpr (std::is_trivially_default_constructible_v<ValueType>)
return ranges::next(first, last); // пропустить инициализацию
I rollback{first};
try
{
for (; !(first == last); ++first)
::new (const_cast<void*>(static_cast<const volatile void*>
(std::addressof(*first)))) ValueType;
return first;
}
catch (...) // откат: уничтожить созданные элементы
{
for (; rollback != first; ++rollback)
ranges::destroy_at(std::addressof(*rollback));
throw;
}
}
template<прямой-диапазон-без-исключения R>
requires std::default_initializable<ranges::range_value_t<R>>
ranges::borrowed_iterator_t<R>
operator()(R&& r) const
{
return (*this)(ranges::begin(r), ranges::end(r));
}
};
inline constexpr uninitialized_default_construct_fn uninitialized_default_construct{};
|
Пример
#include <cstring>
#include <iostream>
#include <memory>
#include <string>
int main()
{
struct S { std::string m{ "▄▀▄▀▄▀▄▀" }; };
constexpr int n{4};
alignas(alignof(S)) char out[n * sizeof(S)];
try
{
auto first{reinterpret_cast<S*>(out)};
auto last{first + n};
std::ranges::uninitialized_default_construct(first, last);
auto count{1};
for (auto it{first}; it != last; ++it)
std::cout << count++ << ' ' << it->m << '\n';
std::ranges::destroy(first, last);
}
catch (...) { std::cout << "Исключение!\n"; }
// Обратите внимание, что для "тривиальных типов" uninitialized_default_construct
// обычно не заполняет нулями данную неинициализированную область памяти.
constexpr char etalon[]{'A', 'B', 'C', 'D', '\n'};
char v[]{'A', 'B', 'C', 'D', '\n'};
std::ranges::uninitialized_default_construct(std::begin(v), std::end(v));
if (std::memcmp(v, etalon, sizeof(v)) == 0)
{
std::cout << " ";
// Возможно неопределённое поведение, вплоть до CWG 1997:
// for (const char c : v) { std::cout << c << ' '; }
for (const char c : etalon)
std::cout << c << ' ';
}
else
std::cout << "Не определено\n";
}
Возможный вывод:
1 ▄▀▄▀▄▀▄▀
2 ▄▀▄▀▄▀▄▀
3 ▄▀▄▀▄▀▄▀
4 ▄▀▄▀▄▀▄▀
A B C D
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| LWG 3870 | C++20 | этот алгоритм может создавать объекты в const хранилище
|
запрещено |
Смотрите также
| создаёт объекты инициализацией по умолчанию в неинициализированной области памяти, определяемой началом и количеством (ниблоид) | |
| создаёт объекты инициализацией значением в неинициализированной области памяти, определяемой диапазоном (ниблоид) | |
| создаёт объекты инициализированные значением в неинициализированной области памяти, определяемой началом и количеством (ниблоид) | |
| создаёт объекты инициализацией по умолчанию в неинициализированной области памяти, определяемой диапазоном (шаблон функции) |