Boost logo

Boost :

From: Ivan Matek (libbooze_at_[hidden])
Date: 2024-12-18 09:12:36


On Tue, Dec 17, 2024 at 3:08 PM Vinnie Falco <vinnie.falco_at_[hidden]> wrote:

> On Mon, Dec 16, 2024 at 10:46 PM Ivan Matek <libbooze_at_[hidden]> wrote:
>
>> On Mon, Dec 16, 2024 at 6:24 PM Vinnie Falco <vinnie.falco_at_[hidden]>
>> wrote:
>>
>>> Can this be implemented as a free function over the public API of
>>> unordered containers? In other words, this signature:
>>>
>> Yes, but it is not like Barry or other people
>> <https://www.youtube.com/watch?feature=shared&t=150&v=kye4aD-KvTU>
>> wanting member functions do not know this.
>>
>
> It seems there are two choices here:
>
> 1. Add a member function to uncountably many existing and future unordered
> containers by adding a member function
>
> 2. Write a single, separate free function template which works for all
> existing and future unordered containers
>
> The member function requires selecting an optional type and including its
> header, while the free function approach can scale to different optional
> types (one per free function).
>
> Please explain why we should prefer 1 instead of 2.
>
>
There are two related points here

1. member syntax is better for users
2. existing practice

Member Syntax is Better for Users
Herb's UFCS P3021
<https://open-std.org/JTC1/SC22/WG21/docs/papers/2023/p3021r0.pdf> has a
list of reasons explaining this in detail. Since some people mentioned UFCS
here before just to make clear: I am linking this paper *not* because of
UFCS, but because it explains why
member syntax is better for users(sections 3.1.1, 3.1.2, 3.1.3).

Existing Practice
We have
std::basic_string<CharT,Traits,Allocator>::starts_with
std::basic_string_view<CharT,Traits>::starts_with
although there is(or more precisely there will be)
std::ranges::starts_with

You may say that having member function 2 times is fine since it duplicated
just 2 times, but we also have

std::set<Key,Compare,Allocator>::contains
std::map<Key,T,Compare,Allocator>::contains
std::unordered_set<Key,Hash,KeyEqual,Allocator>::contains
std::unordered_map<Key,T,Hash,KeyEqual,Allocator>::contains
std::multiset<Key,Compare,Allocator>::contains
std::multimap<Key,T,Compare,Allocator>::contains
std::unordered_multiset<Key,Hash,KeyEqual,Allocator>::contains
std::unordered_multimap<Key,T,Hash,KeyEqual,Allocator>::contains

If you say this is just some modern C++ nonsense:
Since C++98 containers had empty member function although it is trivially
implementable with free function empty. Here not even talking about
"complicated" C++17 std::empty that understands C arrays/std::array/...,
talking about simple function that could just take container with size
member function.
template<typename C>
bool empty(const C& c) { return c.size() == 0; }

But for decades we have this duplication in member functions since empty is
commonly called function.

To recap situation is that in C++ we have tension between ease of use of
common operations on container vs blowing up it's API size. And as Barry
wrote 2 most common operations on map are insertion and lookup. So I
believe lookup is definitely important enough to get
nicer syntax.

few notes regarding suggested free function discussed:

   1. Dispatching to member may be confusing. std::ranges::find /
   std::ranges::contains does not
<https://stackoverflow.com/a/75687947/> dispatch
   to members if available
   2. try_find seems like a wrong name. try_at makes sense since at has
   precondition(unless you use exceptions for control flow :) ), while
find does
   not, i.e. it can fail. So it is kind of weird to have a prefix that says
   try in algorithm that has same "success rate" as find.


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