std::ranges::uninitialized_move_n, std::ranges::uninitialized_move_n_result
| Определено в заголовочном файле <memory>
|
||
| Сигнатура вызова |
||
template< std::input_iterator I, прямой-итератор-без-исключения O, ограничитель-без-исключения-для<O> S > requires std::constructible_from<std::iter_value_t<O>, std::iter_rvalue_reference_t<I>> uninitialized_move_n_result<I, O> uninitialized_move_n( I ifirst, std::iter_difference_t<I> n, O ofirst, S olast ); |
(1) | (начиная с C++20) |
| Вспомогательные типы |
||
template< class I, class O > using uninitialized_move_n_result = ranges::in_out_result<I, O>; |
(2) | (начиная с C++20) |
Перемещает N элементов из входного диапазона, начиная с ifirst, в неинициализированное хранилище, обозначенное диапазоном [ofirst, olast), где N равно min(n, ranges::distance(ofirst, olast)).
Эффект эквивалентен:
for (; n-- > 0 && ofirst != olast; ++ifirst, ++ofirst)
::new (static_cast<void*>(std::addressof(*first)))
std::remove_reference_t<std::iter_reference_t<O>>(ranges::iter_move(ifirst));
Если во время инициализации генерируется исключение, то объекты, которые уже созданы в [ofirst, olast), уничтожаются в неопределённом порядке. Кроме того, объекты во входном диапазоне, начинающемся с ifirst, которые уже были перемещены, остаются в допустимом, но неопределённом состоянии.
Функционально-подобные объекты, описанные на этой странице, являются ниблоидами, то есть:
- Явные списки аргументов шаблона не могут быть указаны при вызове любого из них.
- Ни один из них не виден для поиска, зависящего от аргумента.
- Когда какой-либо из них обнаруживается обычным неквалифицированным поиском по имени слева от оператора вызова функции, поиск, зависящий от аргумента запрещён.
На практике они могут быть реализованы как функциональные объекты или со специальными расширениями компилятора.
Параметры
| ifirst | — | начало входного диапазона элементов, из которого перемещать элементы |
| ofirst, olast | — | пара итератор-ограничитель, обозначающая выходной диапазон элементов для инициализации |
| n | — | количество элементов для перемещения |
Возвращаемое значение
{ifirst + N, ofirst + N}.
Сложность
Линейная по N.
Исключения
Исключение, генерируемое при создании элементов в целевом диапазоне, если таковые имеются.
Примечание
Реализация может повысить эффективность ranges::uninitialized_move_n, например, используя ranges::copy_n, если тип значения выходного диапазона это TrivialType.
Возможная реализация
struct uninitialized_move_n_fn
{
template<std::input_iterator I, прямой-итератор-без-исключения O,
ограничитель-без-исключения-для<O> S>
requires std::constructible_from<std::iter_value_t<O>,
std::iter_rvalue_reference_t<I>>
ranges::uninitialized_move_n_result<I, O>
operator()(I ifirst, std::iter_difference_t<I> n, O ofirst, S olast) const
{
O current{ofirst};
try
{
for (; n-- > 0 && current != olast; ++ifirst, ++current)
::new (const_cast<void*>(static_cast<const volatile void*>
(std::addressof(*current)))) std::remove_reference_t<
std::iter_reference_t<O>>(ranges::iter_move(ifirst));
return {std::move(ifirst), std::move(current)};
}
catch (...) // откат: уничтожить созданные элементы
{
for (; ofirst != current; ++ofirst)
ranges::destroy_at(std::addressof(*ofirst));
throw;
}
}
};
inline constexpr uninitialized_move_n_fn uninitialized_move_n{};
|
Пример
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
void print(auto rem, auto first, auto last)
{
for (std::cout << rem; first != last; ++first)
std::cout << std::quoted(*first) << ' ';
std::cout << '\n';
}
int main()
{
std::string in[]{ "Диагностика", "Не", "Требуется", };
print("изначально, in: ", std::begin(in), std::end(in));
if (
constexpr auto sz = std::size(in);
void* out = std::aligned_alloc(alignof(std::string), sizeof(std::string) * sz)
)
{
try
{
auto first{static_cast<std::string*>(out)};
auto last{first + sz};
std::ranges::uninitialized_move_n(std::begin(in), sz, first, last);
print("после перемещения, in: ", std::begin(in), std::end(in));
print("после перемещения, out: ", first, last);
std::ranges::destroy(first, last);
}
catch (...)
{
std::cout << "Исключение!\n";
}
std::free(out);
}
}
Возможный вывод:
изначально, in: "Диагностика" "Не" "Требуется"
после перемещения, in: "" "" ""
после перемещения, out: "Диагностика" "Не" "Требуется"
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| LWG 3870 | C++20 | этот алгоритм может создавать объекты в const хранилище
|
запрещено |
Смотрите также
(C++20) |
перемещает диапазон объектов в неинициализированную область памяти (ниблоид) |
(C++17) |
перемещает ряд объектов в неинициализированную область памяти (шаблон функции) |