从 unordered_map 移动到 unique_ptr 值的最佳方式
Best way to move from unordered_map with unique_ptr values
我有一张这样的地图
std::unordered_map<std::string, std::unique_ptr<V>> map;
我想最终 运行 映射中所有剩余 V
的函数,所以我执行以下操作:
for (auto&& [_, v] : map) {
func(std::move(v));
}
这行得通,但我一直天真地假设编译器会忽略键值,因为它们没有在 for 循环中使用。现在我认为迭代器产生 const std::string
作为第一个参数,所以确实有很多不必要的字符串构建正在进行。这里到底发生了什么(关于键值)?有没有办法避免任何字符串复制?
在 C++ 标准中,[stmt.ranged] 指定基于范围的 for 循环与某个普通的 for 循环具有等效的语义。在您的情况下,这是:
{
auto &&__range = map ;
auto __begin = __range.begin() ;
auto __end = __range.end() ;
for ( ; __begin != __end; ++__begin ) {
auto&& [_, v] = *__begin;
func(std::move(v));
}
}
(请注意 :
右侧的表达式或 braced-init-list 成为 auto &&__range
的初始化器,而声明器或 :
左侧的结构化绑定用 *__begin
初始化。这就是一般转换的工作方式。)
在这个完整的表格中,我们可以准确地看到发生了什么。每次取消引用迭代器时,它只会生成对存储在映射中的 (string
, unique_ptr
) 对的引用。然后结构化绑定声明使 _
成为引用 string
的左值,而 v
成为引用 unique_ptr
的左值。最后,std::move
将左值转换为右值,并将该右值传递给 func
。没有复制字符串。
取消对 std::unordered_map
returns 引用的迭代器的引用。然后,具有 auto&&
的结构化绑定指向一个引用,因此不会生成任何副本。
我喜欢使用的一个工具是 C++ Insights。 For this example,我们可以看到所有变量都是引用,并且没有在循环中实例化副本:
int main()
{
std::unordered_map<std::string, std::unique_ptr<int> > map = ...;
{
std::unordered_map<std::basic_string<char, std::char_traits<char>, ...> & __range1 = map;
std::__detail::_Node_iterator<...> __begin1 = __range1.begin();
std::__detail::_Node_iterator<...> __end1 = __range1.end();
for(; std::__detail::operator!=(__begin1, __end1); __begin1.operator++())
{
std::pair<const std::basic_string<...>, std::unique_ptr<int, ...> & __operator11 = __begin1.operator*();
const std::basic_string<...>& _ = std::get<0UL>(__operator11);
std::unique_ptr<int, ...>& v = std::get<1UL>(__operator11);
func(std::unique_ptr<int, std::default_delete<int> >(std::move(v)));
}
}
}
我有一张这样的地图
std::unordered_map<std::string, std::unique_ptr<V>> map;
我想最终 运行 映射中所有剩余 V
的函数,所以我执行以下操作:
for (auto&& [_, v] : map) {
func(std::move(v));
}
这行得通,但我一直天真地假设编译器会忽略键值,因为它们没有在 for 循环中使用。现在我认为迭代器产生 const std::string
作为第一个参数,所以确实有很多不必要的字符串构建正在进行。这里到底发生了什么(关于键值)?有没有办法避免任何字符串复制?
在 C++ 标准中,[stmt.ranged] 指定基于范围的 for 循环与某个普通的 for 循环具有等效的语义。在您的情况下,这是:
{
auto &&__range = map ;
auto __begin = __range.begin() ;
auto __end = __range.end() ;
for ( ; __begin != __end; ++__begin ) {
auto&& [_, v] = *__begin;
func(std::move(v));
}
}
(请注意 :
右侧的表达式或 braced-init-list 成为 auto &&__range
的初始化器,而声明器或 :
左侧的结构化绑定用 *__begin
初始化。这就是一般转换的工作方式。)
在这个完整的表格中,我们可以准确地看到发生了什么。每次取消引用迭代器时,它只会生成对存储在映射中的 (string
, unique_ptr
) 对的引用。然后结构化绑定声明使 _
成为引用 string
的左值,而 v
成为引用 unique_ptr
的左值。最后,std::move
将左值转换为右值,并将该右值传递给 func
。没有复制字符串。
取消对 std::unordered_map
returns 引用的迭代器的引用。然后,具有 auto&&
的结构化绑定指向一个引用,因此不会生成任何副本。
我喜欢使用的一个工具是 C++ Insights。 For this example,我们可以看到所有变量都是引用,并且没有在循环中实例化副本:
int main()
{
std::unordered_map<std::string, std::unique_ptr<int> > map = ...;
{
std::unordered_map<std::basic_string<char, std::char_traits<char>, ...> & __range1 = map;
std::__detail::_Node_iterator<...> __begin1 = __range1.begin();
std::__detail::_Node_iterator<...> __end1 = __range1.end();
for(; std::__detail::operator!=(__begin1, __end1); __begin1.operator++())
{
std::pair<const std::basic_string<...>, std::unique_ptr<int, ...> & __operator11 = __begin1.operator*();
const std::basic_string<...>& _ = std::get<0UL>(__operator11);
std::unique_ptr<int, ...>& v = std::get<1UL>(__operator11);
func(std::unique_ptr<int, std::default_delete<int> >(std::move(v)));
}
}
}