026: insert()

insert() を定義します。

おそらく全関数中、最も実装が大変な関数です。
insert()、emplace()、erase()の3つの関数は、要素のスライドが必要です。即ち、insert() の場合ですと、挿入する位置より後ろの要素は、一つずつ後ろにズレる必要があります。このズラす処理を、insert() と合わせて実装する必要があります。
また、insert() は5種類あるので、作業量的にも実装は大変です。

template <typename T>
class myvector
{
...
private:
    using slide_switcher = realloc_switcher;

    ...

public:
    /**
     * @brief      Inserts value before pos.
     * @param[in]  pos: Iterator before which the content will be inserted. pos may
     *                  be the end() iterator.
     * @param[in]  value: Element value to insert.
     * @return     Iterator pointing to the inserted value.
     * @throw      std::length_error: If new capacity is greater than max_size().
     * @throw      std::bad_alloc: If malloc() (or realloc()) fails to allocate storage.
     */
    iterator insert(const_iterator pos, const value_type& value)
    {
        // calculate distance
        size_type distance = mydistance(static_cast<const_iterator>(&heap_[0]), pos, std::random_access_iterator_tag());

        // re-allocate if needed
        if (need_twice_capacity()) {
            reallocation(twice_length(), realloc_switcher());
        }

        // slide elements after pos
        incremental_slide(distance, 1, slide_switcher());
        // set inserted element
        new(&heap_[distance]) value_type(value);
        size_++;

        return static_cast<iterator>(&heap_[distance]);
    }

    /**
     * @brief      Inserts value before pos.
     * @param[in]  pos: Iterator before which the content will be inserted. pos may
     *                  be the end() iterator.
     * @param[in]  value: Element value to insert.
     * @return     Iterator pointing to the inserted value.
     * @throw      std::length_error: If new capacity is greater than max_size().
     * @throw      std::bad_alloc: If malloc() (or realloc()) fails to allocate storage.
     */
    iterator insert(const_iterator pos, value_type&& value)
    {
        // calculate distance
        size_type distance = mydistance(static_cast<const_iterator>(&heap_[0]), pos, std::random_access_iterator_tag());

        // re-allocate if needed
        if (need_twice_capacity()) {
            reallocation(twice_length(), realloc_switcher());
        }

        // slide elements after pos
        incremental_slide(distance, 1, slide_switcher());
        // set inserted element
        new(&heap_[distance]) value_type(std::forward<value_type&&>(value));
        size_++;

        return static_cast<iterator>(&heap_[distance]);
    }

    /**
     * @brief      Inserts count copies of the value before pos.
     * @param[in]  pos: Iterator before which the content will be inserted. pos may
     *                  be the end() iterator.
     * @param[in]  count: The number of copies.
     * @param[in]  value: Element value to insert.
     * @return     Iterator pointing to the inserted value.
     * @throw      std::length_error: If new capacity is greater than max_size().
     * @throw      std::bad_alloc: If malloc() (or realloc()) fails to allocate storage.
     */
    iterator insert(const_iterator pos, size_type count, const value_type& value)
    {
        // calculate distance
        size_type distance = mydistance(static_cast<const_iterator>(&heap_[0]), pos, std::random_access_iterator_tag());

        if (count == 0) {
            return mynext(&(heap_[0]), distance, std::random_access_iterator_tag());
        }

        // re-allocate if needed
        size_type new_cap = expand_size(size_, count);
        if (new_cap > capacity_) {
            reallocation(new_cap, realloc_switcher());
        }

        // slide elements after pos
        incremental_slide(distance, count, slide_switcher());
        // set inserted element
        for (size_type i = 0; i < count; i++) {
            new(&heap_[distance + i]) value_type(value);
        }
        size_ += count;

        return static_cast<iterator>(&heap_[distance]);
    }

    /**
     * @brief      Inserts elements from range [first, last) before pos.
     * @param[in]  pos: Iterator before which the content will be inserted. pos may
     *                  be the end() iterator.
     * @param[in]  first: The first of the range of elements to insert, can't be iterators
     *                    into container for which insert is called.
     * @param[in]  last: The last of the range of elements to insert, can't be iterators
     *                   into container for which insert is called.
     * @tparam     InputIt: Iterator to the elements.
     * @return     Iterator pointing to the inserted value.
     * @throw      std::length_error: If new capacity is greater than max_size().
     * @throw      std::bad_alloc: If malloc() (or realloc()) fails to allocate storage.
     */
    template <class InputIt>
    iterator insert(const_iterator pos, InputIt first,
                    typename std::enable_if<not std::is_integral<InputIt>::value, InputIt>::type last)
    {
        // Switcher for mydistance(). Whether InputIt is random access iterator or input iterator.
        using iterator_type = typename std::iterator_traits<InputIt>::iterator_category;

        // calculate distance
        size_type distance = mydistance(static_cast<const_iterator>(&heap_[0]), pos, std::random_access_iterator_tag());

        if (first == last) {
            return mynext(&(heap_[0]), distance, std::random_access_iterator_tag());
        }

        // calculate count
        size_type count = mydistance(first, last, iterator_type());

        // re-allocate if needed
        size_type new_cap = expand_size(size_, count);
        if (new_cap > capacity_) {
            reallocation(new_cap, realloc_switcher());
        }

        // slide elements after pos
        incremental_slide(distance, count, slide_switcher());
        // set inserted element
        pointer p = (&heap_[distance]);
        for (InputIt i = first; i != last; ++i) {
            new(p++) value_type(*i);
        }
        size_ += count;

        return static_cast<iterator>(&heap_[distance]);
    }

    /**
     * @brief      Inserts elements from initializer list ilist before pos.
     * @param[in]  pos: Iterator before which the content will be inserted. pos may
     *                  be the end() iterator.
     * @param[in]  ilist: Initializer list to insert the values from.
     * @return     Iterator pointing to the inserted value.
     * @throw      std::length_error: If new capacity is greater than max_size().
     * @throw      std::bad_alloc: If malloc() (or realloc()) fails to allocate storage.
     */
    iterator insert(const_iterator pos, std::initializer_list<value_type> ilist)
    {
        // calculate distance
        size_type distance = mydistance(static_cast<const_pointer>(&heap_[0]), pos, std::random_access_iterator_tag());

        if (ilist.size() == 0) {
            return mynext(&(heap_[0]), distance, std::random_access_iterator_tag());
        }

        // re-allocate if needed
        size_type new_cap = expand_size(size_, ilist.size());
        if (new_cap > capacity_) {
            reallocation(new_cap, realloc_switcher());
        }

        // slide elements after pos
        incremental_slide(distance, ilist.size(), slide_switcher());
        // set inserted element
        pointer p = (&heap_[distance]);
        for (const auto& i: ilist) {
            new(p++) value_type(i);
        }
        size_ += ilist.size();

        return static_cast<iterator>(&heap_[distance]);
    }

    ...

private:
    /**
     * @brief      Creates and checks new container size.
     * @param[in]  size: current size. should be size_.
     * @param[in]  count: expanded size.
     * @return     New number of elements..
     * @throw      std::length_error: If (size + count) overflows  or (size + count)
     *                                is greater than the maximum size.
     */
    size_type expand_size(size_type size, size_type count) const
    {
        size_type new_cap = size + count;
        if ((new_cap < size) || (new_cap < count)) {
            throw std::length_error("myvecotr::length_check()");
        }
        if (new_cap > MAX_SIZE) {
            throw std::length_error("myvecotr::length_check()");
        }

        return new_cap;
    }

    /**
     * @brief      Does slide elements into incremental direction.
     *             value_type shall be TriviallyCopyable.
     * @param[in]  pos: Element index which is start position of moving.
     * @param[in]  count: The size of the elements to be moved.
     * @param[in]  trivially_copyable_tag: Function switcher according to value_type characteristic.
     */
    void incremental_slide(size_type pos, size_type count, trivially_copyable_tag)
    {
        for (size_type i = size_ + count - 1; i >= pos + count; i--) {
            heap_[i] = heap_[i - count];
        }
    }

    /**
     * @brief      Does slide elements into incremental direction.
     *             value_type shall be MoveConstructible.
     * @param[in]  pos: Element index which is start position of moving.
     * @param[in]  count: The size of the elements to be moved.
     * @param[in]  move_constructible_tag: Function switcher according to value_type characteristic.
     */
    void incremental_slide(size_type pos, size_type count, move_constructible_tag)
    {
        for (size_type i = size_ + count - 1; i >= pos + count; i--) {
            new(&heap_[i]) value_type(std::move(heap_[i - count]));
            heap_[i - count].~value_type();
        }
    }

    /**
     * @brief      Does slide elements into incremental direction.
     *             value_type shall be CopyConstructible.
     * @param[in]  pos: Element index which is start position of moving.
     * @param[in]  count: The size of the elements to be moved.
     * @param[in]  copy_constructible_tag: Function switcher according to value_type characteristic.
     */
    void incremental_slide(size_type pos, size_type count, copy_constructible_tag)
    {
        for (size_type i = size_ + count - 1; i >= pos + count; i--) {
            new(&heap_[i]) value_type(heap_[i - count]);
            heap_[i - count].~value_type();
        }
    }
};
補助関数を1+3つ作成します。
expand_size() は複数個の要素が挿入された後のサイズを取得する関数です。ここでは、capacity と size が同じになるようにします。(capacity を倍々ゲームで増やす戦略もありです。)
incremental_slide() は挿入に合わせて要素を後ろへズラします。後ろへズラす処理なので、要素はindexが大←小の方向へ代入されます。代入に要する演算子に応じて、3つの関数を用意しています。

insert() の中身は、①挿入後のサイズ計算、②領域確保、③既存要素の移動、④新要素の代入、という流れになります。これは5つの insesrt() どれも共通です。


全ソースコード: https://github.com/suomesta/myvector/tree/master/026

0 件のコメント:

コメントを投稿