STL map::insert 应该支持 move_iterators 的移动语义吗?
Should STL map::insert support move semantics with move_iterators?
我有大量数据要从文件加载到 map
中,该文件已经按排序顺序排列(因为它是从地图序列化的)。
我发现首先将数据加载到 vector
,然后使用 insert
批量加载到 map
会更快。这在 19 秒的加载时间中节省了一秒多一点。
value_type
是一个包含其他结构向量的结构。加载完成后我不需要 vector
,所以我在调用 map::insert
时使用了 move_iterator
。使用 MSVC 2015 (VC14),数据不会移动到地图中,而是通过 const_reference
深入到 STL 代码中进行复制。
这是一个符合标准的实现,忽略数据的移动吗?
template<typename Stream, typename Key, typename Type, typename Traits, typename Allocator>
bool const read(Stream &is, std::map<Key, Type, Traits, Allocator> &map)
{
size_t size;
read(is, size);
std::vector<std::pair<Key, Type>> items(size);
for (size_t i=0; i<size; ++i)
{
auto &item = items[i];
if (!read(is, item.first) || !read(is, item.second))
return false;
}
map.insert(std::make_move_iterator(items.begin()), std::make_move_iterator(items.end()));
return !!is;
}
我已经解决了将 map.insert
替换为
的问题
for (auto &item : items)
map.insert(std::move(item));
虽然不是很整洁,但确实又节省了 0.6 秒。
Is this a standard-compliant implementation, to ignore the moving of data?
标准说 insert()
函数:
Requires: value_type
shall be EmplaceConstructible
into X
from *i
.
因此必须可以从迭代器的 reference
类型构造 value_type
,在本例中是右值引用。这意味着您可以使用仅移动(即不可复制)键类型和映射类型,只要迭代器 returns 右值可以转换为映射的 value_type
它就必须工作。
应该使用构造函数 std::pair<const Key, Type>::pair<K2, T2>(pair<K2, T2>&&)
将来自迭代器的右值构造到映射中的值。
这对我适用于 GCC,以及使用在线编译器的最新 VC++:
#include <vector>
#include <map>
#include <iterator>
struct MoveOnly {
MoveOnly() = default;
MoveOnly(MoveOnly&&) = default;
};
bool operator<(const MoveOnly&, const MoveOnly&) { return false; }
template<typename Key, typename Type, typename Traits, typename Allocator>
void read(std::map<Key, Type, Traits, Allocator> &map)
{
std::vector<std::pair<Key, Type>> items(1);
map.insert(std::make_move_iterator(items.begin()), std::make_move_iterator(items.end()));
}
int main()
{
std::map<MoveOnly, MoveOnly> m;
read(m);
}
如果您指定了自己的移动构造函数或移动赋值运算符,则应将其标记为'noexcept'。否则标准容器将复制元素。
在此处查看文档:http://en.cppreference.com/w/cpp/language/noexcept_spec
我有大量数据要从文件加载到 map
中,该文件已经按排序顺序排列(因为它是从地图序列化的)。
我发现首先将数据加载到 vector
,然后使用 insert
批量加载到 map
会更快。这在 19 秒的加载时间中节省了一秒多一点。
value_type
是一个包含其他结构向量的结构。加载完成后我不需要 vector
,所以我在调用 map::insert
时使用了 move_iterator
。使用 MSVC 2015 (VC14),数据不会移动到地图中,而是通过 const_reference
深入到 STL 代码中进行复制。
这是一个符合标准的实现,忽略数据的移动吗?
template<typename Stream, typename Key, typename Type, typename Traits, typename Allocator>
bool const read(Stream &is, std::map<Key, Type, Traits, Allocator> &map)
{
size_t size;
read(is, size);
std::vector<std::pair<Key, Type>> items(size);
for (size_t i=0; i<size; ++i)
{
auto &item = items[i];
if (!read(is, item.first) || !read(is, item.second))
return false;
}
map.insert(std::make_move_iterator(items.begin()), std::make_move_iterator(items.end()));
return !!is;
}
我已经解决了将 map.insert
替换为
for (auto &item : items)
map.insert(std::move(item));
虽然不是很整洁,但确实又节省了 0.6 秒。
Is this a standard-compliant implementation, to ignore the moving of data?
标准说 insert()
函数:
Requires:
value_type
shall beEmplaceConstructible
intoX
from*i
.
因此必须可以从迭代器的 reference
类型构造 value_type
,在本例中是右值引用。这意味着您可以使用仅移动(即不可复制)键类型和映射类型,只要迭代器 returns 右值可以转换为映射的 value_type
它就必须工作。
应该使用构造函数 std::pair<const Key, Type>::pair<K2, T2>(pair<K2, T2>&&)
将来自迭代器的右值构造到映射中的值。
这对我适用于 GCC,以及使用在线编译器的最新 VC++:
#include <vector>
#include <map>
#include <iterator>
struct MoveOnly {
MoveOnly() = default;
MoveOnly(MoveOnly&&) = default;
};
bool operator<(const MoveOnly&, const MoveOnly&) { return false; }
template<typename Key, typename Type, typename Traits, typename Allocator>
void read(std::map<Key, Type, Traits, Allocator> &map)
{
std::vector<std::pair<Key, Type>> items(1);
map.insert(std::make_move_iterator(items.begin()), std::make_move_iterator(items.end()));
}
int main()
{
std::map<MoveOnly, MoveOnly> m;
read(m);
}
如果您指定了自己的移动构造函数或移动赋值运算符,则应将其标记为'noexcept'。否则标准容器将复制元素。 在此处查看文档:http://en.cppreference.com/w/cpp/language/noexcept_spec