46

Structured bindings make it more clean and readable to iterate through a map with a range based for loop like below

for (auto [key, value] : map) {
    cout << key << " : " << value << endl;
}

But can structured bindings be used in lambda expressions like the following?

std::for_each(map.begin(), map.end(), [](auto [key, value]) {
    cout << key << " : " << value << endl;
});

From what it looks like the above code does not work with the online C++ compiler I found here https://wandbox.org/permlink/sS6r7JZTB3G3hr78.

If it does not work then is there a good reason the above is not supported? Or is it just something that has not been proposed yet? The template will only be instantiated on use, so the structured bindings' "unbinding" process can occur where the instantiation is requested (i.e. when the function is called)

Enlico
  • 23,259
  • 6
  • 48
  • 102
Curious
  • 20,870
  • 8
  • 61
  • 146
  • [The section for structured binding doesn't explicitly disallow it, if it's worth anything](https://timsong-cpp.github.io/cppwp/n4659/dcl.struct.bind). – StoryTeller - Unslander Monica Aug 07 '17 at 07:41
  • 4
    But the grammar for function declarations doesn't allow it either. – StoryTeller - Unslander Monica Aug 07 '17 at 07:45
  • @StoryTeller can you elaborate on that further? – Curious Aug 07 '17 at 07:46
  • 2
    I mean that the grammar description for function declarators (which is quite a long read) doesn't make explicit allowance to plop a structured binding there in place of a single parameter. So as it's not allowed explicitly, compilers reject it. I suppose a proposal *could* be made to enhance the grammar, if it wasn't made already. – StoryTeller - Unslander Monica Aug 07 '17 at 07:48
  • @StoryTeller ah I understand what you mean, ok – Curious Aug 07 '17 at 07:48
  • 4
    Whenever you throw something into function (template) signatures, you have to answer: 1) how do you mangle this stuff? 2) how does overload resolution work? 3) what part of it should SFINAE and what part of it should be a hard error? These are hardly trivial questions. For structured binding in particular, there's the additional complication that the "arity" can change due to a `tuple_size` specialization. – T.C. Aug 07 '17 at 09:02
  • @T.C. could you explain what you mean by that last part? (the "arity" part) – Curious Aug 07 '17 at 14:23
  • https://wandbox.org/permlink/veHbQjAROw2h76K4 – T.C. Aug 07 '17 at 16:31

2 Answers2

33

This is not currently allowed by the syntax; structured bindings are a simple-declaration:

simple-declaration:[...]
- attribute-specifier-seqopt decl-specifier-seq ref-qualifieropt [ identifier-list ] initializer ;

while function parameters are introduced by a parameter-declaration-list, which contains declarators:

The declarators specify the names of these entities and (optionally) modify the type of the specifiers with operators such as * (pointer to) and () (function returning).

That is, a structured binding is a (block-level) statement syntax - you can see this by noting that the grammar for it ends in a semicolon ;. Allowing structured bindings in a lambda parameter list would require additional grammar to be added.

It sounds like a good idea, and I can't immediately see any ambiguity in the syntax; it would certainly be worth discussing as it solves your presented use case nicely and more concisely than the alternatives.

ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • 1
    Is there any chance that this will be implemented in C++20? I didn't find anything on the quick. – pasbi Mar 31 '19 at 10:49
  • 9
    @pasbi There was actually a paper to address this issue (p0931), it didn't go through unfortunately because of ambiguity with arrays in lambdas (`[](auto [constant]) {}`) and lots of things being in flux with concepts. There might be a chance in the future in C++23, but seems hard for this to get into C++20 :( – Curious May 04 '19 at 06:31
4

I think this would be a great proposal (if isn't there one already) that would simplify code for algorithms that pass zip iterators/tuples of references. (for example https://github.com/correaa/iterator-zipper)

At the same time is seems that it is not something that you couldn't achieve using a bit more verbose code by extracting the structure in the first line of the function:

#include <algorithm>
#include <iostream>
#include <map>

using std::cout;
using std::endl;

int main(){
    auto map = std::map<int, int>{{1, 2}};
    std::for_each(map.begin(), map.end(), [](auto const& key_value){
        auto const& [key, value] = key_value;
        cout<< key <<" "<< value <<endl;
    });
}

https://wandbox.org/permlink/sS6r7JZTB3G3hr78

(which strengthens the point that this is implementable and simple to add to the language).

A more streamlined version could be indented as:

    [](auto const& _){auto&& [key, value] = _; // [](auto const& [key, value]){
        cout<< key <<" "<< value <<endl;
    }
alfC
  • 14,261
  • 4
  • 67
  • 118
  • 4
    There was a proposal for this http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0931r0.pdf. But it ended up getting pushback in Jacksonville that year because the syntax of the structured binding decomposition was ambiguous with a similar array parameter https://wandbox.org/permlink/nkcENj2G2kuW31wb. Sadly I haven't had time to followup on it further. – Curious Oct 30 '20 at 18:25
  • 2
    @Curious I see. Once again arrays getting in the way of progress. I think this wouldn’t be a problem if brackets would prefer to expand/initialize a tuple preferentially. – alfC Oct 30 '20 at 23:20