如何在std::copy算法中使用std::make_move_iterator?

How to use std::make_move_iterator in std::copy algorithm?

我有一个问题:

当我尝试通过 std::copy 算法将数据从(vector with unique_ptr)移动到(list with unique_ptr)时成功编译但随后在 运行 中粉碎!这是一个代码:


std::vector<std::unique_ptr<std::string>> vecUPTRstring;
std::unique_ptr<std::string> ptrStringArr1(new std::string("qwe"));
std::unique_ptr<std::string> ptrStringArr2(new std::string("asd"));
vecUPTRstring.push_back(std::move(ptrStringArr1));
vecUPTRstring.push_back(std::move(ptrStringArr2));

std::list<std::unique_ptr<std::string>> listUPTRstring;

std::copy(std::make_move_iterator(vecUPTRstring.begin()),
                    std::make_move_iterator(vecUPTRstring.end()),
                    std::make_move_iterator(listUPTRstring.begin())
                );
for (auto& var : listUPTRstring)
{
    cout << "list value = " << var << endl;
}

崩溃错误为:

Expression: cannot dereference end list iterator

我已经检查了不同的修复方法并阅读了一些文章,包括 ,但我仍然需要帮助来解决这个问题。

仅使 source 迭代器移动迭代器,而目标迭代器应该是插入器,因为您的列表是空的。如果目标容器中已有 n 元素并希望用源元素覆盖它们,则只能将 dest.begin() 作为目标传递。

std::copy(std::make_move_iterator(vecUPTRstring.begin()),
          std::make_move_iterator(vecUPTRstring.end()),
          std::inserter(listUPTRstring, listUPTRstring.end()));

但是,我还必须建议 std::move instead of std::copy with move iterators as it's semantically equal 和 simpler/clearer:

std::move(vecUPTRstring.begin(),
          vecUPTRstring.end(),
          std::inserter(listUPTRstring, listUPTRstring.end()));

列表使用类似指针的间接方式保存值,并且在 std::list 中存储像指针这样的小元素几乎永远不会比使用向量快,只有极少数例外。因此,首先,您应该检查使用 std::list 是否真的可以节省您的时间。您还应该检查使用 std::unique_ptr 是否可以节省您的时间。

仅当基准测试证明使用间接寻址提高了性能时才引入间接寻址。 默认情况下,按值将内容保存在向量中。仅当测量显示有益时才使用其他容器和内存管理层。我的意思是测量。现代 CPU 的性能通常受内存访问 模式 的限制,缓存未命中很容易淹没“浪费”复制连续缓存行的任何感知“开销”。初步估计,顺序内存访问是免费的。

具体到字符串时,以下类型的表现从最好到最差,最后一个非常糟糕,尤其是当字符串很小时:

  • std::vector<std::string>
  • std::vector<std::unique_ptr<std::string>>
  • std::list<std::string>
  • std::list<std::unique_ptr<std::string>>

但是如果你真的想使用一个列表,并在列表的生命后期做很多inserts/removes(无论如何使用该数据类型的唯一原因),它可能会更便宜支付 move/copy 值一次进入列表的成本,然后通过使用值而不是显式指针享受较低间接性的好处。

无论如何,你绝对应该对所有这些进行基准测试,因为在大多数情况下,std::list 在没有缓存的微控制器上是有意义的,一旦你在上下文中 运行大型应用程序和 PC 上的活动堆 -class CPU 在过去二十年的某个时候,它变成了性能灾难。

template <typename T>
std::list<T> ptrVectorToList(std::vector<std::unique_ptr<T>> && src)
{
  std::list<T> list;
  for (auto &ptr : src) {
    list.emplace_back(std::move(*ptr));
    ptr.reset();
  }
  src.clear();
  return list;
}