C++ Logo

std-proposals

Advanced search

Re: Yet another member function for std::map

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Wed, 28 Jul 2021 15:38:40 -0400
On Wed, Jul 28, 2021 at 3:20 PM Edward Catmur via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Wed, 28 Jul 2021 at 20:16, Kyle Knoepfel via Std-Proposals <
> std-proposals_at_[hidden]> 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

Received on 2021-07-28 14:39:16