簡體   English   中英

為什么 std::copy() 需要 std::back_inserter 插入具有足夠容量的向量?

[英]Why does std::copy() require std::back_inserter to insert into a vector with sufficient capacity?

參考代碼:

#include <vector>
#include <algorithm>
#include <string>
#include <iostream>

void print(std::string label, std::vector<int> & arr) {
  std::cout << label << ":" << " size: " << arr.size() << " cap: " << arr.capacity() << " [ ";
  for (auto elem : arr) {
    std::cout << elem << " ";
  }
  std::cout << " ] " << std::endl;
}

void reserve_dest_use_begin() {
  std::vector<int> s_arr = {0, 1, 2, 3, 4, 5}; 
  print("source", s_arr);

  std::vector<int> d_arr;
  d_arr.reserve(3);
  print("dest", d_arr);

  auto min_elems = std::min(s_arr.size(), d_arr.capacity());

  std::cout << "COPYING FIRST" << min_elems << "3 FROM SOURCE TO DEST" << std::endl;

  std::copy(s_arr.begin(), s_arr.begin() + min_elems, d_arr.begin());

  print("source", s_arr);
  print("dest", d_arr);
}

void reserve_dest_use_back_inserter() {
  std::vector<int> s_arr = {0, 1, 2, 3, 4, 5}; 
  print("source", s_arr);

  std::vector<int> d_arr;
  d_arr.reserve(3);
  print("dest", d_arr);

  auto min_elems = std::min(s_arr.size(), d_arr.capacity());

  std::cout << "COPYING FIRST" << min_elems << " ELEMENTS FROM SOURCE TO DEST" << std::endl;

  std::copy(s_arr.begin(), s_arr.begin() + min_elems, std::back_inserter(d_arr));

  print("source", s_arr);
  print("dest", d_arr);
}

int main() {
  std::cout << "RESERVE DEST ARR. USE BEGIN() TO COPY" << std::endl;
  reserve_dest_use_begin();
  std::cout << "RESERVE DEST ARR. USE BACK_INSERTER() TO COPY" << std::endl;
  reserve_dest_use_back_inserter();

輸出:

RESERVE DEST ARR USE BEGIN() TO COPY
source: size: 6 cap: 6 [ 0 1 2 3 4 5  ] 
dest: size: 0 cap: 3 [  ] 
COPYING FIRST 3 ELEMENTS FROM SOURCE TO DEST
source: size: 6 cap: 6 [ 0 1 2 3 4 5  ] 
dest: size: 0 cap: 3 [  ] 
=============================================
RESERVE DEST ARR USE BACK_INSERTER() TO COPY
source: size: 6 cap: 6 [ 0 1 2 3 4 5  ] 
dest: size: 0 cap: 3 [  ] 
COPYING FIRST 3 ELEMENTS FROM SOURCE TO DEST
source: size: 6 cap: 6 [ 0 1 2 3 4 5  ] 
dest: size: 3 cap: 3 [ 0 1 2  ]

在這兩種情況下,目標陣列都有足夠的容量。 cppreference的文檔指出:

Copies the elements in the range, defined by [first, last), to another range beginning at d_first.
1) Copies all elements in the range [first, last) starting from first and proceeding to last - 1. The behavior is undefined if d_first is within the range [first, last). In this case, std::copy_backward may be used instead.

d_arr.begin()指向[first, last)源范圍之外的范圍,但在提供的示例中,我需要使用std::back_inserter()進行復制,而不僅僅是提供d_arr.begin()盡管底層向量有足夠的容量。

std::back_inserter()操作是優化為只記錄內存塊,還是推回每個元素? cppreference 的注釋指出:

In practice, implementations of std::copy avoid multiple assignments and use bulk copy functions such as std::memmove if the value type is TriviallyCopyable and the iterator types satisfy LegacyContiguousIterator.

但是,對於std::back_inserter()我懷疑它沒有用memmove優化。

總之,我有以下問題:

  1. 當底層向量有足夠的容量時,為什么我不能使用d_arr.begin()作為std::copyOutputIt
  2. 使用std::back_inserter()針對批量復制范圍進行了優化?

編輯:我想我是從錯誤的角度提出這個問題的。 在評論中已經澄清,我想做的操作是insert()而不是copy() 我的具體用例是我反復clear()我的d_arr並將子向量從s_arr復制到d_arr 我試圖避免重新分配我的d_arr 然而,由於d_arr被清除,雖然它有足夠的容量,但它沒有大小,這意味着沒有要復制的元素。 相反,我真正想做的是將s_arr的子向量插入d_arr

d_arr.insert(d_arr.begin(), s_arr.begin(), s_arr.begin() + min_elems)

當底層向量有足夠的容量時,為什么我不能使用 d_arr.begin() 作為 std::copy 中的 OutputIt?

因為目標向量是空的,因此std::copy溢出向量(因為任何元素的賦值都在空向量的邊界之外)。

std::back_inserter() 操作是否優化為僅 memmove

它可能是。 它甚至可能被優化為更快的東西。

使用 std::back_inserter() 是否針對批量復制范圍進行了優化?

鑒於足夠聰明的優化器,是的。


使用適當的 vector 構造函數比std::copy更簡單。 既適用於代碼的閱讀者,也適用於優化者。

如果我在代碼中表達一個簡單的向量可能會有所幫助。

template <class T>
class Vector
{
private:
    unsigned int size = 0;
    unsigned int capacity = 10;
    T* array = new T[10];

public:
    void push_back(T const& item) {
        if (size >= capacity) {
            reserve(capacity * 2);
        }
        array[size] = item;
        ++size;
    }

    void reserve(unsigned int newCapacity) {
        if (size > capacity) {
            T* const temp = new T[newCapacity];
            std::copy(cbegin(), cend(), temp);
            delete [] std::exchange(array, temp);
            capacity = newCapacity;
        }
    }

    T& operator[](unsigned int i) { return *array[i]; }

    // iterators
    T* begin() { return array; }
    T* end() { return array + size; }
    T const* cbegin() const { return array; }
    T const* cend() const { return array + size; }
};

你看到的是, reserve確實增加了向量的分配大小,但不會改變大小!

當您在print中使用基於范圍的 for 循環時,將使用beginend迭代器,它們在內部使用向量的大小。 因此不會訪問整個內部數組。

盡管使用自定義端點( s_arr.begin() + min_elems )直接寫入數組,但有足夠的容量這樣做可能不是“非法”,這根本不是一個好習慣。

編輯:什么改變矢量的大小resize ,其內部看起來有點像這樣

    void resize(unsigned int newSize, T const& val = T{}){
        if (newSize > capacity) {
            reserve(newSize);
        }
        if (newSize > size) {
            std::fill(begin()+size, begin()+newSize, val);
        }
        size = newSize;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM