代码之家  ›  专栏  ›  技术社区  ›  Krupip

如何处理将初始化值的一部分移动到分配中重叠的部分初始化和部分未初始化空间中?

  •  0
  • Krupip  · 技术社区  · 2 年前

    背景

    上下文中,我试图在自定义向量类型上实现std::vector insert。这适用于的所有重载 vector::insert ,我试图保持与C++的std::vector的奇偶性。

    在我被迫重新分配内存的情况下,插入实际上是微不足道的。当我 不要 不过,当我的容量>=时,需要重新分配插入元素所需的新尺寸,我很困惑。我在这里使用列表 https://en.cppreference.com/w/cpp/container/vector/insert

    在我 也许 没有微不足道的可复制类型,以及 我可以 没有默认的可构造类型,并且我 也许 没有可移动类型,那么我不太确定如何正确初始化数据。

    例如,对于以下原型

    constexpr iterator insert(const_iterator pos, const T &value);
    

    如果插入 value 不会触发分配的大小调整,那么这意味着我可以只使用已经进行的分配。

    我通常会伸手去拿这样的东西 copy_backwards ,但问题是,如果我这样做,我不知道类型是否 便宜地 如果可以的话,我应该选择可移动的。虽然这对输入没有任何意义 价值 ,仍然应该被复制,我很困惑这里需要发生什么,例如,假设我已经

    pointer_type m_ptr; 
    size_type m_size; 
    size_type m_capacity;
    allocator_type m_allocator; 
    

    我将尝试这样的操作来移动已经有效的数据。

    if constexpr (std::is_move_constructible_v<T>) {
        auto pos_idx = static_cast<size_type>(std::distance(cbegin(), pos));
        std::move_backward(m_ptr + pos_idx, m_ptr + m_size, m_ptr + pos_idx + 1);
        ...
    }
    

    但问题是,我正在使用最后一个元素进入未初始化的内存。所以我可以先对最后一个元素进行未初始化的移动。

    if constexpr (std::is_move_constructible_v<T>) {
        std::uninitialized_move(m_ptr + m_size - 1, m_ptr + size, m_ptr + size); 
        auto pos_idx = static_cast<size_type>(std::distance(cbegin(), pos));
        std::move_backward(m_ptr + pos_idx, m_ptr + m_size - 1, m_ptr + pos_idx + 1);
        ...
    }
    

    但现在最后一个元素的空间假设是未初始化的?所以我不能再向后移动了。

    这意味着我需要做一个 对超过插入点的每个值进行未初始化的移动 正确的但我不仅需要这样做,我还需要 为每个值手动调用未初始化的move函数 ,因为我需要有效地做 std::uninitalized_move_backward(...) (希望任何看过这篇文章的人都会意识到这是不存在的),因为uninit std函数都不是重叠安全的?即:

    if constexpr (std::is_move_constructible_v<T>) {
        auto pos_idx = static_cast<size_type>(std::distance(cbegin(), pos));
        auto pos_end_reverse = pos != begin() ? (std::reverse_iterator(pos - 1) : rend()); 
        for(auto pos_r = rbegin(); pos_r != pos_end_reverse; ++pos_r){
            //subtract, because this is a reverse iterator and we are trying to look *forward*. 
            std::uninitialized_move_n(pos_r, 1, pos_r - 1); 
        } 
    } 
    

    问题

    对于我有一些初始化的值需要移动到分配中重叠的初始化和未初始化空间的情况,有没有办法手动向后调用每个元素的未初始化移动,或者有没有stdlib方法来处理这种情况?

    1 回复  |  直到 2 年前
        1
  •  1
  •   Sam Varshavchik    2 年前

    更好地理解这里的需求会使所需的语义比看起来简单得多:只需提醒自己,移动会使移动的from对象处于“有效但未指定的状态”。这意味着移动的from对象是 处于“未初始化”状态(如您所称)。

    这意味着,在插入过程中,只有重新定位到上一个有效(初始化)值之后的新值才会移动到未初始化的空间。

    使用一个简单的例子:现有向量有六个值, vector[0] 通过 vector[5] ,并且在向量的开头插入两个新值。

    这意味着 矢量[5] vector[7] vector[4] vector[6] 是进入未初始化的空间。所有其他动作, vector[3] 矢量[5] 。它们实际上是一种调动任务(因为 矢量[5] ,及其前代保留在“有效但未指定的空间”中)。

    总之,在没有重新分配的情况下进行的插入最终会导致三部分操作:1)未初始化的移动到向量生长到的所有保留的、未初始化的空间中,2)对所有其他移动的值进行移动分配,3)对插入的向量中的新值进行移动指派。

    而且,不,没有标准的库算法可以为您处理所有这些,所有这些逻辑都必须实现。但其实并没有那么复杂。