如何在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;
}
我有一个问题:
当我尝试通过 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;
}