As suggested in a reply to my previous proposal, I drafted a proposal to introduce the std::is_uniqued() function.

Since it was developed from Arthur's idea, I would be very happy if you took part in the proposal.


I. Motivation

The std::is_uniqued() function can be used to check if a range of elements has no consecutive equal elements.

Even if there is already the std::adjacent_find() function, that can be used to get the same effect, the introduction of the function std::is_uniqued makes the check clearer, like std::contains() and std::is_sorted() do.


II. Impact on the standard

This proposal simply adds new standard functions into the algorithm header. It does not require any changes in core language. It has been implemented in standard C++.


III. Design decisions

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


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


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


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


Checks if the elements in the range [first, last) are unique.

A sequence is uniqued with respect to a binary predicate pred if any iterator it pointing to the sequence and the consecutive ++it iterator pointing to an element in the sequence, pred(*it, *(++it)) evaluates to false.

1) elements are compared using operator==. The behavior is undefined if it is not an equivalence relation.

2) elements are compared using the given binary predicate pred. The behavior is undefined if it is not an equivalence relation.

3, 4) Same as (1, 2), but executed according to policy. These overloads do not partecipate in overload resolution unless std::is_execution_policy_v<std::remove_cvref_t<ExecutionPolicy>> is true.


Preconditions:

ForwardIt must meet the requirements of LegacyForwardIterator.


Complexity:

At most std::distance(first, last).


Exceptions:

If execution of a function invoked as part of the algorithm throws an exception and ExecutionPolicy is one of the standard policies, std::terminate is called. For any other ExecutionPolicy, the behavior is implementation-defined.

If the algorithm fails to allocate memory, std::bad_alloc is thrown.


/* Ranges */

template< std::forward_iterator I, std::sentinel_for<I> S, class Proj =
std::identity, std::indirect_equivalence_relation 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_equivalent_relation
<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 = {} );


1) elements are compared using the given binary predicate pred.

2) Same as (1), but uses r as the source range, as if using ranges::begin(r) as first and ranges::end(r) as last.


Complexity:

1) At most std::distance(first, last).

2) At most ranges::distance(r).