On Wed, Jul 28, 2021 at 3:20 PM Edward Catmur via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
On Wed, 28 Jul 2021 at 20:16, Kyle Knoepfel via Std-Proposals <std-proposals@lists.isocpp.org> wrote:

Right...which is why I said the return type of value_for(...) would not be quite like std::optional.  So there seem to be four or five options to me:

1. std::map<K, V>::value_for(K const&) -> typename std::map<K, V>::value_handle, where map gains a nested type that behaves like std::optional<V&> would behave.  Yuck.
2. std::map<K, V>::value_for(K const&) -> V*, where a bare pointer, or some equivalent, is returned.  Also yuck.
3. std::map<K, V>::value_for(K const&) -> std::optional<V&>.  Requires convincing the standards committee to support reference-type template arguments for std::optional.
4. Something else I haven't thought of
5. Stop wanting this feature

I think 3 is the ideal solution, but the most unlikely to occur.  Option 1 is doable but I assume no one wants to add an additional type to std::map.  I fear 5 is the most likely solution, although it would sadden me.

4. optional<reference_wrapper<V>>? 

2, "return bare pointer," is correct. The STL already uses this idiom in `std::get_if` and `std::any_cast`, as well as `std::dynamic_cast<T*>`.
Actually, many industry codebases already have a convenience function along these lines. Serendipitously, I just added one to HyperRogue the other day:
https://github.com/zenorogue/hyperrogue/pull/246/files#diff-f42f1abd2e52db6ab3f7da5cc6c2e290adc46e1d7cd47c673ea49f5ead738c96R851

However, notice that there are many operations that you'd still want to do via third-party convenience functions. For example, in the exact same HyperRogue patch, I also introduced a helper function `span_at`, with these semantics:

template<class Map, class Key, class T = /*metaprogramming*/>
span<const T> span_at(const Map& map, const Key& key) {
    auto it = map.find(key);
    return (it == map.end()) ? span<const T>() : span<const T>(it->second.data(), it->second.size());
}

This is useful for things like
    const std::map<Parent, std::vector<Child>> m = ...;
    Parent p = ...;
    for (const Child& c :  span_at(m, p)) { ... }
where a naïve
    for (const Child& c : m[p])
would try to insert into `m`.
(And yes there's prior art for this API in optional::value_or. But `value_or(m, p, span<T>())` would have been much too verbose, and would have had trouble with type-checking, so for this codebase we want the shorter simpler version. Which goes to illustrate what I think is my point: helper methods are super useful but also have way too many knobs to be worth standardizing. Just like type-erased callable wrappers, every codebase is going to want to make their own tradeoffs and control their own code.)

my $.02,
Arthur