
Neil Groves wrote:
On Sat, 21 Dec 2024 at 12:11, Peter Dimov via Boost
mailto:boost@lists.boost.org > wrote: That's why I'm always defining a helper function `lookup` that returns a pointer, which allows me to write this instead:
if( auto p = lookup( map, key ) ) { // do something with *p }
If there's a built-in way to obtain an optional
instead of an iterator, I can use that instead of `lookup`, without changing the syntax. What I tried to do was provide that with map.equal_range(key) | mapped_value | invoke(fn). And then tidy the syntax, but I accept that wasn't an outstanding success! I collapsed the map.equal_range and mapped_value into one step and then we had map | mapped_values(key) | invoke([](...) {})
In the end what I had for you paraphrasing you snippet above would be:
map | mapped_values(key) | invoke([&](auto&& p) { // do something with p };
That's nice enough. I would probably have called the first adaptor `values_for_key` instead of `mapped_values`. We can also collapse the two into `invoke_for_key`. This is only part of the story though. First, you need to specify what's the result of `| invoke`, can I chain additional adaptors after it, and if so, what range do they see. Second, my example allows for an `else` clause: if( auto p = lookup( map, key ) ) { // do something with *p } else { // handle the case when `key` isn't in `map` } Third, in my example, you can `return` from the branches, whereas in yours, `return` would return from the lambda. (`break`, `continue` pose similar problems.) E.g. for(auto key: keys ) { if( auto p = lookup( map, key ) ) return *p; }