45 Intermediate The Hundred
The Loop That Copied the Whole Map
the & that doesn't save you a thing
int main() {
std::map<std::string, int> m{{"maximum-connection-retries", 3}};
const auto& stored = *m.begin(); // the element actually living in the map
std::cout << std::boolalpha << std::left;
for (const std::pair<std::string, int>& e : m)
std::cout << "pair<std::string, int>& same value? " << std::setw(8)
<< (&e.second == &stored.second) << "same key buffer? "
<< (e.first.data() == stored.first.data()) << '\n';
for (const auto& e : m)
std::cout << "auto& same value? " << std::setw(8)
<< (&e.second == &stored.second) << "same key buffer? "
<< (e.first.data() == stored.first.data()) << '\n';
}
Both loops bind e by const&. Do both print true?
Answer
pair<std::string, int>& same value? false same key buffer? false
auto& same value? true same key buffer? true
The first loop never sees the map's elements at all — it copies each one.
Why¶
A std::map<K, V>'s value_type is std::pair<const K, V>, not std::pair<K, V> —
the key is const so you can't quietly break the ordering invariant. That makes
const std::pair<std::string, int>& a reference to a different type, which triggers the
oldest rule in the book: a const& that can't bind directly gets a converted temporary
to bind to instead. So each iteration copy-constructs a whole pair, key string and all;
over a 1000-entry map with long keys that is 1000 heap allocations to read 1000 ints,
where the auto& loop makes zero. Happily, GCC 11 does flag this under plain -Wall:
warning: loop variable ‘e’ of type ‘const std::pair<std::__cxx11::basic_string<char>, int>&’
binds to a temporary constructed from type
‘std::pair<const std::__cxx11::basic_string<char>, int>’ [-Wrange-loop-construct]
The fix¶
Let the compiler spell the element type, or spell it exactly:
for (const auto& e : m) // value_type&, no copy
for (const auto& [key, count] : m) // C++17, same thing, nicer names
for (const std::pair<const std::string, int>& e : m) // the honest long form
Takeaway: a map element is pair<const K, V> — say const auto& and let the compiler
get the const right for you.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo — add -Wall to get the warning.