2

I'm trying to do something like this:

const std::map<char, int> histogram{
    {'A', 2},
    {'D', 1},
    {'M', 1},
};

for (const auto& [index, key, value] : std::views::enumerate(histogram))
{
    // ...
}

but it doesn't compile (Compiler Explorer) reporting error:

<source>:13:21: error: 3 names provided for structured binding
   13 |     for (const auto& [index, key, value] : std::views::enumerate(histogram))
      |                     ^~~~~~~~~~~~~~~~~~~
<source>:13:21: note: while 'const std::tuple<long int, const std::pair<const char, int>&>' decomposes into 2 elements

Naively using [index, [key, value]] of course didn't work either...

Is there some other trick I'm missing or in this case I just cannot "unpack" the map element and instead have to keep it like [index, entry] and then use entry.first and entry.second?

Or maybe <ranges> offers something else than enumerate for this scenario?

6
  • I am not sure what you're trying to do, maps don't have indices, they have key/value pairs (and no specified order). So what do you think you need the index for? Maybe your histogram should just be a std::vector<std::size_t> and use the (unsigned!) char values as indices directly Commented Oct 1 at 7:54
  • @PepijnKramer anything really. For example, print items on... numerated list? In the loop differently treat every N-th item. Build some other data? It seems there are many possible uses. Commented Oct 1 at 11:22
  • I get that about indices, but for map it is how the keys are sorted that define the order. And it is the key that contains the real info, not the "index" at which they are stored (other than you will get some sorted order). For a histogram with only A and C you will find A at your "index 0", and C at your "index 1" (not at index 0 and 2 like you might expect). As @wohlstad shows. So I am not focused on the syntax of your code but the semantics which troubles me (and which also may not be important for your use case at all). Commented Oct 1 at 11:34
  • 1
    @PepijnKramer I agree the current code might not present the best case for such a question. But I actually read the question to be a general one regarding enumerate and structured binding, which might be useful in other cases as well. And my answer can be adapted to apply to any enumerate result. Commented Oct 1 at 11:44
  • @wohlstad I understood that part, and yes I agree and enumerate has its uses :) but the code-reviewer in me also checks semantics and there is room for confusion there. Commented Oct 1 at 12:12

2 Answers 2

6

Structured binding can "unpack" only a single level - in your case it would be to the index and entry in the map.

You can solve it by adding a second structured binding to "unpack" the entry in the map:

for (const auto& [index, entry] : std::views::enumerate(histogram)) 
{
    // Second binding:
    const auto& [key, value] = entry;

    std::cout << "index: " << index << ", key: " << key << ", value: " << value << '\n';
}

Output:

index: 0, key: A, value: 2
index: 1, key: D, value: 1
index: 2, key: M, value: 1

Live demo

Sign up to request clarification or add additional context in comments.

Comments

3

You can use views::keys and views::values ​​to assemble into a new tuple(i, x, y) like

  const std::map<char, int> histogram{
      {'A', 2},
      {'D', 1},
      {'M', 1},
  };

  for (const auto& [index, key, value] : 
    std::views::zip(std::views::iota(0uz, histogram.size()),
                    std::views::keys(histogram),
                    std::views::values(histogram)))
  {
    // ...
  }

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.