This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++23 status.
{forward_,}list::unique
Section: 23.3.9.5 [list.ops], 23.3.7.6 [forward.list.ops] Status: C++23 Submitter: Tim Song Opened: 2017-07-07 Last modified: 2023-11-22
Priority: 3
View all other issues in [list.ops].
View all issues with C++23 status.
Discussion:
There are various problems with the specification of list::unique
and its forward_list
counterpart, some of which are obvious even on cursory inspection:
first
and last
, which aren't even defined.i - 1
on non-random-access iterators - in the case of forward_list
, on forward-only iterators.std::unique
to require an equivalence relation
and adjusting the order of comparison, weren't applied to these member functions.list::unique
, was closed as NAD with the rationale that
"All implementations known to the author of this Defect Report comply with these assumption", and "no impact on current code is expected", i.e. there is no evidence of real-world confusion or harm.That implementations somehow managed to do the right thing in spite of obviously defective standardese doesn't seem like a good reason to not fix the defects.
[2017-07 Toronto Tuesday PM issue prioritization]
Priority 3; by the way, there's general wording in 26.2 [algorithms.requirements] p10 that lets us specify iterator arithmetic as if we were using random access iterators.
[2017-07-11 Tim comments]
I drafted the P/R fully aware of the general wording in 26.2 [algorithms.requirements] p10. However, that general wording is limited to Clause 28, so to make use of the shorthand permitted by that wording, we would need additional wording importing it to these subclauses.
Moreover, that general wording only definesa+n
and b-a
; it notably doesn't define a-n
, which is needed here. And one cannot merely define a-n
as
a+(-n)
since that has undefined behavior for forward iterators.
Previous resolution [SUPERSEDED]:
This wording is relative to N4659.
Edit 23.3.9.5 [list.ops] as indicated:
void unique(); template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);-?- Requires: The comparison function shall be an equivalence relation.
-19- Effects: Ifempty()
, has no effects. Otherwise, eErases all but the first element from every consecutive group ofequalequivalent elements referred to by the iteratori
in the rangefor which
[first + 1, last)[next(begin()), end())(for the version of
*i == *(i-1)*j == *iunique
with no arguments) or(for the version of
pred(*i, *(i - 1))pred(*j, *i)unique
with a predicate argument) holds, wherej
is an iterator in[begin(), end())
such thatnext(j) == i
. Invalidates only the iterators and references to the erased elements. -20- Throws: Nothing unless an exception is thrown bythe equality comparison or the predicate. -21- Complexity: If*i == *(i-1)
orpred(*i, *(i - 1))
the range[first, last)
is not empty!empty()
, exactlyapplications of the corresponding predicate, otherwise no applications of the predicate.
(last - first) - 1size() - 1Edit [forwardlist.ops] as indicated:
void unique(); template <class BinaryPredicate> void unique(BinaryPredicate binary_pred);-?- Requires: The comparison function shall be an equivalence relation.
-16- Effects: Ifempty()
, has no effects. Otherwise, eErases all but the first element from every consecutive group ofequalequivalent elements referred to by the iteratori
in the rangefor which
[first + 1, last)[next(begin()), end())(for the version with no arguments) or
*i == *(i-1)*j == *i(for the version with a predicate argument) holds, where
pred(*i, *(i - 1))pred(*j, *i)j
is an iterator in[begin(), end())
such thatnext(j) == i
. Invalidates only the iterators and references to the erased elements. -17- Throws: Nothing unless an exception is thrown by the equality comparison or the predicate. -18- Complexity: Ifthe range[first, last)
is not empty!empty()
, exactlyapplications of the corresponding predicate, otherwise no applications of the predicate.
(last - first) - 1distance(begin(), end()) - 1
[2021-02-21 Tim redrafts]
The wording below incorporates editorial pull request 4465.
[2021-05-21; Reflector poll]
Set status to Tentatively Ready after five votes in favour during reflector poll.
[2021-06-07 Approved at June 2021 virtual plenary. Status changed: Voting → WP.]
Proposed resolution:
This wording is relative to N4878.
Edit 23.3.9.5 [list.ops] as indicated:
-1- Since lists allow fast insertion and erasing from the middle of a list, certain operations are provided specifically for them.222 In this subclause, arguments for a template parameter named
[…]Predicate
orBinaryPredicate
shall meet the corresponding requirements in 26.2 [algorithms.requirements]. The semantics ofi + n
andi - n
, wherei
is an iterator into the list andn
is an integer, are the same as those ofnext(i, n)
andprev(i, n)
, respectively. Formerge
andsort
, the definitions and requirements in 26.8 [alg.sorting] apply.void unique(); template<class BinaryPredicate> void unique(BinaryPredicate binary_pred);-?- Let
-?- Preconditions:binary_pred
beequal_to<>{}
for the first overload.binary_pred
is an equivalence relation. -20- Effects: Erases all but the first element from every consecutive group ofequalequivalent elements. That is, for a nonempty list, erases all elements referred to by the iteratori
in the rangefor which
[first + 1, last)[begin() + 1, end())*i == *(i-1)
(for the version ofunique
with no arguments) orbinary_pred(*i, *(i - 1))
istrue
(for the version of. Invalidates only the iterators and references to the erased elements. -21- Returns: The number of elements erased. -22- Throws: Nothing unless an exception is thrown byunique
with a predicate argument) holdsthe predicate. -23- Complexity: If*i == *(i-1)
orpred(*i, *(i - 1))
the range[first, last)
is not emptyempty()
isfalse
, exactlyapplications of the corresponding predicate, otherwise no applications of the predicate.
(last - first) - 1size() - 1
Edit [forwardlist.ops] as indicated:
-1- In this subclause, arguments for a template parameter named
[…]Predicate
orBinaryPredicate
shall meet the corresponding requirements in 26.2 [algorithms.requirements]. The semantics ofi + n
, wherei
is an iterator into the list andn
is an integer, are the same as those ofnext(i, n)
. The expressioni - n
, wherei
is an iterator into the list andn
is an integer, means an iteratorj
such thatj + n == i
istrue
. Formerge
andsort
, the definitions and requirements in 26.8 [alg.sorting] apply.void unique(); template<class BinaryPredicate> void unique(BinaryPredicate binary_pred);-?- Let
-?- Preconditions:binary_pred
beequal_to<>{}
for the first overload.binary_pred
is an equivalence relation. -18- Effects: Erases all but the first element from every consecutive group ofequalequivalent elements. That is, for a nonempty list, erases all elements referred to by the iteratori
in the rangefor which
[first + 1, last)[begin() + 1, end())*i == *(i-1)
(for the version with no arguments) orbinary_pred(*i, *(i - 1))
istrue
(for the version with a predicate argument) holds. Invalidates only the iterators and references to the erased elements. -19- Returns: The number of elements erased. -20- Throws: Nothing unless an exception is thrown bythe equality comparison orthe predicate. -21- Complexity: Ifthe range[first, last)
is not emptyempty()
isfalse
, exactlyapplications of the corresponding predicate, otherwise no applications of the predicate.
(last - first) - 1distance(begin(), end()) - 1