与foreach在地图上的意外副本

时间:2013-12-20 17:55:16

标签: c++ c++11 map foreach auto

我正在尝试遍历地图的条目,我得到了意想不到的副本。这是程序:

#include <iostream>
#include <map>
#include <string>

struct X
{
    X()
    {
        std::cout << "default constructor\n";
    }

    X(const X&)
    {
        std::cout << "copy constructor\n";
    }
};

int main()
{
    std::map<int, X> numbers = {{1, X()}, {2, X()}, {3, X()}};
    std::cout << "STARTING LOOP\n";
    for (const std::pair<int, X>& p : numbers)
    {
    }
    std::cout << "ENDING LOOP\n";
}

这是输出:

default constructor
copy constructor
default constructor
copy constructor
default constructor
copy constructor
copy constructor
copy constructor
copy constructor
STARTING LOOP
copy constructor
copy constructor
copy constructor
ENDING LOOP

为什么我在循环中获得三个副本?如果我使用类型推断,副本就会消失:

for (auto&& p : numbers)
{
}

这里发生了什么?

3 个答案:

答案 0 :(得分:15)

map<K,V>的值类型为pair<const K,V>;因此,您的循环需要将pair<const int,X>转换为pair<int,X>,同时复制键和值,以便为您提供对该类型的引用。

使用正确的类型(明确指定,或使用auto推断)将删除副本。

答案 1 :(得分:5)

副本是由于您正在迭代地图但绑定错误的引用类型。该地图的value_typestd::pair<const int, X>(请注意const)。因为它们不是同一类型,所以编译器正在创建一个临时的并绑定引用。

在大多数情况下,您可以(可能应该)使用auto&const auto&进行迭代,这样可以避免此类问题。如果您想拼出该类型,可以使用嵌套的value_type或确切的类型:

for (const std::map<int,X>::value_type& r : numbers) {
// or
for (const std::pair<const int,X>& r : numbers) {

首选项应为:const auto& r然后是const std::map<...>::value_type&,然后是const std::pair<const int, X>&。请注意,列表中的右侧更远,您提供的知识越多,编译器就越少帮助您。

答案 2 :(得分:4)

value_type中的std::map<K, V> std::pair<K, V>,而是std::pair<K const, V>。也就是说,您无法更改元素的关键组件。如果编译器发现从std::pair<K, V> const&获取std::pair<K const, K> const&的请求,则它使用std::pair<...>的转换构造函数并创建合适的临时值。

我建议使用value_type来避免任何差异:

for (std::map<int, X>::value_type const& p: numbers)
    ...

或者,您可以推断出类型:

for (auto const& p: number)
    ...