On Mon, Apr 10, 2023, 6:52 PM Arthur O'Dwyer <arthur.j.odwyer@gmail.com> wrote:
On Mon, Apr 10, 2023 at 11:59 AM LoS via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
Introduction of value_equal to
std::unordered_map / std::unordered_multimap

I. Motivation
The std::map / std::multimap containers already provides a value_compare member class that
allows to compare two value_type objects using the key_compare comparison object under
the hood. This allows the user to directly call an instance of the value_compare comparison
object and thus perform a compare operation between two value_type objects easily.

More importantly, it allows us to cleanly express the class invariant
    assert(std::is_sorted(v.begin(), v.end(), v.value_comp()));
which is true for all associative containers (set, multiset, map, multimap, flat_set, flat_multiset, flat_map, flat_multimap).
I don't see any such benefit to the proposed `value_eq`.

At best it would let you write
    assert(std::is_uniqued(v.begin(), v.end(), v.value_eq()));
for (unordered_set, unordered_map).
But in that case, I would expect the proposal to also:
- Consider adding the `std::is_uniqued` algorithm to <algorithm>
- Consider adding the `value_equal` member type and `value_eq` member function to (set, map, flat_set, flat_map), where it would be implemented in terms of two calls to `value_compare`

Also note that you're going to get bikeshedding over the choice between `equal/eq` and `equivalent/equiv`. The advantage of the latter is that it's technically correct; the disadvantage is that it's more of a mouthful.

IMHO, this is not worth pursuing. But if you pursue any part of it, IMHO `std::is_uniqued` will turn out to be the actually useful part. (Sure it's redundant with `std::adjacent_find`; but so is `std::contains` redundant with `std::find`, and `std::none_of` redundant with `std::find_if`. Having cleanly defined verbs is nice.)

HTH,
Arthur

Thanks for the reply, O'Dwyer, 
I really appreciate your opinion!
The proposal to introduct the std::is_uniqued algorithm seems very interesting. I suppose it has the following signatures:

template< class ForwardIt >
constexpr bool is_uniqued( ForwardIt first, ForwardIt last );

template< class ExecutionPolicy, class ForwardIt >
bool is_uniqued( ExecutionPolicy&& policy, ForwardIt first, ForwardIt last );

template< class ForwardIt, class BinaryPredicate>
constexpr bool is_uniqued( ForwardIt first, ForwardIt last, BinaryPredicate pred);

template< class ExecutionPolicy, class ForwardIt, class BinaryPredicate>
bool is_uniqued( ExecutionPolicy&& policy, ForwardIt first, ForwardIt last, BinaryPredicate pred);

/* Ranges */

template< std::forward_iterator I, std::sentinel_for<I> S, class Proj = std::identity,
std::indirect_binary_predicate<std::projected<I, Proj>, std::projected<I, Proj>> Pred = ranges::equal_to >
constexpr bool is_uniqued( I first, S last, Pred pred = {}, Proj proj = {} );

template< ranges::forward_range R, class Proj = std::identity,
std::indirect_binary_predicate<std::projected<ranges::iterator_t<R>, Proj>, std::projected<ranges::iterator_t<R>, Proj>> Pred = ranges::equal_to >
constexpr bool
    is_uniqued( R&& r, Pred pred = {}, Proj proj = {} );

However, I do not think the introduction of value_equal into the std::unordered_map / std::unordered_multimap containers brings necessarily to the introduction of them also into the associative containers, such as std::map / std::multimap, because value_equal is concerned with providing an extension of the key_equal object and not synthesizing a binary predicate that performs an equivalence relation. Thus, if the value_equal is introduced into the associative containers, it means the key_equal must be introduced before.
Furthermore, the technical decisions about value_equal were made to be consistent with the already-existing key_equal, therefore it must be used by the unordered associative containers as an equality predicate.