# std::ranges::uninitialized_move_n, std::ranges::uninitialized_move_n_result

< cpp‎ | memory

定义于头文件 `<memory>` |  |
调用签名 |  |
template <std::input_iterator I, no-throw-forward-iterator O, no-throw-sentinel-for<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 起)

从始于 `ifirst` 的输入范围移动 N 个元素到范围 `[ofirst, olast)` 所指代的未初始化存储，其中 N 为 min(n, ranges::distance(ofirst, olast)) 。

效果等价于：

    for (; n-- > 0 && ofirst != olast; ++ifirst, ++ofirst)
        ::new (const_cast<void*>(static_cast<const volatile void*>(std::addressof(*first))))
            std::remove_reference_t<std::iter_reference_t<O>>(ranges::iter_move(ifirst));

若在初始化期间抛出异常，则以未指定顺序析构 `[ofirst, olast)` 中已构造的读写。另外，在始于 `ifirst` 的范围中的已被移动的对象被置于合法但未指定的状态。

此页面上描述的仿函数实体是 niebloid ，即：

- 调用它们中的任何一者时不可指定显式模板实参。
- 它们无一对实参依赖查找可见。
- 在通常无限定查找寻找函数调用运算符左侧的名称找到它们之一时，它禁止实参依赖查找。

实际上，它们能以函数对象，或以某些特殊编译器扩展实现。

### 参数

ifirst | - | 要移动的输入范围的起始
ofirst, olast | - | 要初始化的输入范围
n | - | 要移动的元素数

### 返回值

{ifirst + N, ofirst + N} 。

### 复杂度

与 N 成线性。

### 异常

构造目标范围中的元素时抛出的异常，若存在。

### 注解

若输出范围的值类型为平凡类型 (TrivialType) ，则实现可能提升 `ranges::uninitialized_move_n` 的效率，例如用 ranges::copy_n 。

### 可能的实现

    struct uninitialized_move_n_fn {
        template <std::input_iterator I, no-throw-forward-iterator O,
                 no-throw-sentinel-for<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[] { "No", "Diagnostic", "Required", };
        print("initially, 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("after move, in: ", std::begin(in), std::end(in));
                print("after move, out: ", first, last);

                std::ranges::destroy(first, last);
            }
            catch (...) {
                std::cout << "Exception!\n";
            }
            std::free(out);
        }
    }

可能的输出：

    initially, in: "No" "Diagnostic" "Required"
    after move, in: "" "" ""
    after move, out: "No" "Diagnostic" "Required"

### 参阅

ranges::uninitialized_move (C++20) | 移动对象范围到未初始化的内存区域 (niebloid)
uninitialized_move_n (C++17) | 移动一定数量对象到未初始化内存区域 (函数模板)