4

I have noticed that unpacking keys and values from std::map does not give me references. I am assuming that individual entries in std::map is stored as a pair of const key and value.

What works:

  • Manually taking .second of pair from std::map into reference.
  • Unpacking a pair made using std::make_pair into references.
  • Taking references from result of std::views::values

What doesn't work:

  • Directly unpacking map entries in for loop into references
  • Unpacking map entry obtained from iterator into references

Why do above two not work? Attached is a sample source code, and deduced types from the IDE. The compiler does result in same error messages with the insertions.

#include <map>
#include <type_traits>
#include <ranges>

int main() {
    std::map<int, int> data;

    for (const auto& kv : data) {
        auto& v = kv.second;
        auto& [a, b] = kv;
        static_assert(std::is_reference_v<decltype(v)>);
    }

    for (const auto& v : data | std::views::values)
        static_assert(std::is_reference_v<decltype(v)>);

    for (const auto& [k, v] : data)
        static_assert(std::is_reference_v<decltype(v)>);  // error

    {
        auto& kv = *data.begin();
        auto& [k, v] = kv;
        static_assert(std::is_reference_v<decltype(v)>);  // error
    }

    {
        auto kv = std::make_pair(3, 5);
        auto& k = kv.first;
        auto& v = kv.second;
        static_assert(std::is_reference_v<decltype(v)>);
    }

    return 0;
}

deduced types

cigien
  • 57,834
  • 11
  • 73
  • 112
kizer
  • 75
  • 5
  • 1
    Can you add the exact compiler error messages? Intellisense error squiggles don't have the final say here. – alter_igel Sep 18 '21 at 16:06
  • 1
    This has nothing to do with `std::map`. It's just that the names in a structured binding are not references. Related/dupe https://stackoverflow.com/questions/61340567/why-does-structured-binding-introduce-variables-as-values-not-references – cigien Sep 18 '21 at 16:07
  • @alterigel It's two static assertion failed on squiggled lines. – kizer Sep 18 '21 at 16:08
  • @cigien I can't believe I forgot to check what would result from `auto& [k2, v2] = kv;` in the last block. This does result in two non-references too. – kizer Sep 18 '21 at 16:10
  • The question is why you need a reference here. Each structured binding introduces *one* anonymous variable, and if you use `auto&`, it is a reference. `k` and `v` are its members. You can change `v` and it will reflect back to `map`. – n. m. could be an AI Sep 18 '21 at 16:25

1 Answers1

3

It is a reference. However, there is a special rule that decltype on a structured binding does (from [dcl.type.decltype]/1.1):

  • if E is an unparenthesized id-expression naming a structured binding ([dcl.struct.bind]), decltype(E) is the referenced type as given in the specification of the structured binding declaration;

And the referenced types for a map's pair (which is a pair<Key const, Value>) are just Key const and Value, never any kind of reference.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • *It is a reference.* If `v` is a reference, then `std::is_reference_v` should be true. – n. m. could be an AI Sep 18 '21 at 16:43
  • 2
    [CWG2313](http://www.open-std.org/JTC1/SC22/WG21/docs/cwg_defects.html#2313) changed them to be magic lvalues for the objects bound to the anonymous references. – Davis Herring Sep 18 '21 at 19:29
  • @DavisHerring I feel like that's a distinction without a meaningful different for their usability - they behave like references. It's also pretty unfortunate I think that `decltype(v)` just lies here, because there's no way to forward a structured binding. – Barry Sep 18 '21 at 22:07
  • @n.1.8e9-where's-my-sharem. It is a reference. As my answer says, `decltype(v)` just doesn't give you a reference type, that doesn't change the fact it behaves in every other way like the reference you'd expect. – Barry Sep 18 '21 at 22:08
  • @Barry _they behave like references_ What does it mean: to behave like a reference? – Language Lawyer Nov 06 '21 at 00:22