Boost logo

Boost :

From: Andrey Semashev (andrey.semashev_at_[hidden])
Date: 2024-12-16 11:41:25


On 12/16/24 13:35, Ivan Matek via Boost wrote:
> Hi everybody,
>
> I presume somebody thought of this before but for some reason it never
> happened, but just in case it was never discussed I wonder if people here
> believe adding an nicer version of find(with different name obviously) to
> associative containers in boost would be beneficial?
>
> Since boost::optional allows references this would work.
>
> I presume naming is what most people will have strong feeling about.
> I personally prefer ofind/oat since I love short names, Barry suggested
> different names in his blog
> <https://brevzin.github.io/c++/2023/05/23/map-api/>, e.g. try_at.

As a general comment on the article, I do use the iterator returned from
find(), lower_bound() etc. quite often (e.g. to later pass it to erase()
or as a hint to insert()), so I disagree with the suggestion that an
iterator return is somehow suboptimal. This is the fundamental interface
upon which every other, including the one returning optional, could be
built, if needed. So I see it as the most optimal one, as it provides
the most flexibility to the user.

Adding find() overloads to containers means adding a dependency on
boost::optional, which makes the container a more heavy dependency for
the users. Since containers are often used as data members in classes,
this increase in source code footprint arguably will be fairly visible
across user's code base.

Another downside is that as soon as you choose boost::optional as a
return value, proponents of std types everywhere will complain that the
new interface doesn't compose well in their code bases. You couldn't
choose std::optional as it doesn't support references (yet?), but if it
did at some point, it would mean requiring a very recent C++ version,
which will be a regression in compatibility for the existing Boost
libraries. It would also exacerbate the boost::optional vs.
std::optional argument.

An alternative would be to return a pointer instead of an optional, but
eliminates the main benefit of the addition in the first place.

Which is the addition is a more succinct syntax for testing and
transforming the found value. Personally, I don't see myself using this
syntax very often, if ever, because in my experience the mapped value is
rarely as simple as an int, and I almost never want to construct a
default value when one is not found in the container. My mental model is
that constructing an object has a non-insignificant cost to it, and I
should avoid it, if possible. However, I understand that other people
may have different experiences, and there the proposed syntax could be
more useful.

I don't think the benefits outweigh the costs in this case. It may be
useful to have an external adapter of the same effect though:

  template< typename Range, typename... Args >
  optional< typename Range::const_reference >
  ofind(Range const& r, Args&&... args)
  {
    using result_type = optional< typename Range::const_reference >;
    auto it = r.find(std::forward< Args >(args)...);
    return it == r.end() ? result_type{} : result_type(*it);
  }

Note that I'm intentionally using a variadic form to allow for passing
an ordering function to polymorphic find() e.g. in Boost.Intrusive
containers.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk