I am trying to loop over the entries of a map, and I get unexpected copies. Here is the program:
#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";
}
And here is the output:
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
Why do I get three copies inside the loop? The copies disappear if I use type inference:
for (auto&& p : numbers)
{
}
What's going on here?
The value type of
map<K,V>
ispair<const K,V>
; so your loop needs to convertpair<const int,X>
topair<int,X>
, copying both the key and the value, to give you a reference to that type.Using the correct type (specified explicitly, or deduced with
auto
) will remove the copies.The copies are due to the fact that you are iterating a map but binding the wrong reference type. The
value_type
for that map isstd::pair<const int, X>
(note theconst
). Because they are not the same type, the compiler is creating a temporary and binding the reference.You can (probably should) iterator using
auto&
orconst auto&
in most cases, which will avoid this kind of issues. If you want spell out the type, you can use the nestedvalue_type
or the exact type:The preference should be:
const auto& r
thenconst std::map<...>::value_type&
and thenconst std::pair<const int, X>&
. Note that the further to the right in the list, the more knowledge you are providing and the less you allow the compiler to help you.The
value_type
in astd::map<K, V>
is notstd::pair<K, V>
but ratherstd::pair<K const, V>
. That is, you can't change the key component of the elements. If the compiler finds a request to get astd::pair<K, V> const&
from astd::pair<K const, K> const&
it uses the conversion constructor ofstd::pair<...>
and creates a suitable temporary.I'd recommend using
value_type
instead to avoid any differences:Alternatively you can have the type be deduced: