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 tostd::unordered_map / std::unordered_multimapI. MotivationThe std::map / std::multimap containers already provides a value_compare member class thatallows to compare two value_type objects using the key_compare comparison object underthe hood. This allows the user to directly call an instance of the value_compare comparisonobject and thus perform a compare operation between two value_type objects easily.More importantly, it allows us to cleanly express the class invariantassert(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 writeassert(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.