微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么std :: copy要求std :: back_inserter插入具有足够容量的向量中?

如何解决为什么std :: copy要求std :: back_inserter插入具有足够容量的向量中?

参考代码

#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,d_arr.capacity());

  std::cout << "copYING FirsT" << min_elems << " ELEMENTS FROM SOURCE TO DEST" << std::endl;

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

  print("source",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()进行复制,而不仅仅是提供{{ 1}},尽管基础向量具有足够的容量。

d_arr.begin()操作是否已优化为仅存储内存块,还是推回了每个元素? cppreference中的注释表示:

std::back_inserter()

但是,我怀疑使用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()进行优化。

总而言之,我有以下问题:

  1. 当基础向量具有足够的容量时,为什么不能在memmove中将d_arr.begin()用作OutputIt中的std::copy
  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)

解决方法

当基础向量具有足够的容量时,为什么不能在std :: copy中将d_arr.begin()用作OutputIt?

由于目标向量为空,因此std::copy溢出了该向量(因为任何元素的分配都在空向量的边界之外)。

std :: back_inserter()操作是否已优化为仅记忆

可能是。甚至可以将其优化为更快的速度。

是否使用针对批量复制范围进行了优化的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;
    }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。