おそらく全関数中、最も実装が大変な関数です。
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 件のコメント:
コメントを投稿