[英]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
優化。
總之,我有以下問題:
d_arr.begin()
作為std::copy
的OutputIt
?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 循環時,將使用begin
和end
迭代器,它們在內部使用向量的大小。 因此不會訪問整個內部數組。
盡管使用自定義端點( 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.