Boost logo

Boost :

From: Ivan Matek (libbooze_at_[hidden])
Date: 2024-12-16 12:30:40


On Mon, Dec 16, 2024 at 12:41 PM Andrey Semashev via Boost <
boost_at_[hidden]> wrote:

> 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.
>

Barry never said that iterator find is useless, just that for most usecases
it is an overkill since it returns info most users do not need most of the
time.
So I do not think that doing find and then using it for erase or insert
hint is very common. In my code
for sure more than 60% of usecases would work fine if I never knew of the
iterator.

>
> 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.
>

This is true, on my machine it is 60k LOC, unclear how many of those are
common headers users include on their own very often.
Also it may be possible to cut it down with some tricks that some libraries
use, but that is a per library decision since some boost OWNERS hate
solutions like that and prefer to include everything
as specified by the standard while others do prefer using implementation
headers
<https://github.com/boostorg/move/commit/b7a04d320104dff1f0ed335addadbb65e9a08797>
to speed up compile.

>
> 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.
>

Partially true. It will not compose well if they do pass the returned
boost::optional outside of the function and entire API of their codebase
uses std::optional.
But as mentioned often people just want to use returned optional to perform
some logic on the result, not necessarily return it.
As for std::optional: IDK how to read the progress here
https://github.com/cplusplus/papers/issues/1661 but it may or may not be in
C++26.

>
> 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.
>

If you think value_or is too expensive it is fine to not use it. You still
can benefit from lack of compare to .end()

> 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.
>

I have proposed ofind long time ago on this mailing list, but as linear
search O(n) algorithm :).
Here I would personally not allow Args to be variadic since variadics
usually mean a big drop in quality of error messages, but that is personal
preference.
In general I agree that ofind is best possible solution without containers
providing member functions.

>
>
> _______________________________________________
> Unsubscribe & other changes:
> http://lists.boost.org/mailman/listinfo.cgi/boost
>


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