Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use structured bindings and a for-each loop to iterate through a few “packed-together” values?

Tags:

c++

c++17

I often use initializer lists and for-each loops to iterate through a small number of ad-hoc values, like so:

for (auto x : {1, 2, 6, 24, 120}) {
  do_something(x);
}

I recently tried to write something similar, but with structured bindings and packed-together values instead:

for (auto[dx, dy] : {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
  try_to_move(dx, dy); // nope! won’t compile
}

Unfortunately, this doesn’t compile. Clang tells me:

error: cannot use type ‘void’ as a range

In fact, even something like auto mylist = {{1, 2}, {3, 4}}; won’t compile.

This leaves me with two questions:

  1. Is there an alternative syntax to accomplish what I want in a terse and readable manner?

  2. Why doesn’t the type of auto mylist get parsed as initializer_list<initializer_list<int>>? Wouldn’t that work fine?

like image 382
Presley Graham Avatar asked Oct 11 '25 16:10

Presley Graham


1 Answers

Why doesn’t the type of auto mylist get parsed as initializer_list<initializer_list<int>>? Wouldn’t that work fine?

The reason is that simply no-one proposed it yet.

The handy syntax auto x = {1, 2, 6, 24, 120}; comes from proposal N3912 which was adopted into C++17 (see also N3922).

The deduction process is outlined in [dcl.type.auto.deduct]/4:

If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initialization is copy-list-initialization, with std​::​initializer_­list<U>. Deduce a value for U using the rules of template argument deduction from a function call, where P is a function template parameter type and the corresponding argument is e. If the deduction fails, the declaration is ill-formed.

Since type deduction in a hypothetical function call f({1, 2}) would fail, so too is the nested braced-init-list deduction auto x = { {1, 2}, {3, 4} }; also impossible.

I guess the same trick could be applied to a deduction from a function call, which would make nested initializer_list deduction possible.

So a follow-up proposal is welcome.

Is there an alternative syntax to accomplish what I want in a terse and readable manner?

You could define a good old multidimensional array:

int lst[][2] = { {1, 2}, {3, 4}, {5, 6}, {7, 8} };
for (auto [x, y] : lst) {
    . . .
}

Or as suggested in the comments, give the first pair a type to help the deduction:

for (auto [x, y] : { std::pair{1, 2}, {3, 4}, {5, 6}, {7, 8} }) {
    . . .
}
like image 131
rustyx Avatar answered Oct 14 '25 07:10

rustyx