This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of New status.

4269. unique_copy passes arguments to its predicate backwards

Section: 26.7.9 [alg.unique] Status: New Submitter: Jonathan Wakely Opened: 2025-05-29 Last modified: 2025-05-29

Priority: Not Prioritized

View other active issues in [alg.unique].

View all other issues in [alg.unique].

View all issues with New status.

Discussion:

For the unique algorithms, 26.7.9 [alg.unique] p1 says:

1. Let pred be equal_to{} for the overloads with no parameter pred, and let E be
  1. (1.1) — bool(pred(*(i - 1), *i)) for the overloads in namespace std;
  2. (1.2) — bool(invoke(comp, invoke(proj, *(i - 1)), invoke(proj, *i))) for the overloads in namespace ranges.

However for the unique_copy algorithms, 26.7.9 [alg.unique] p6 says that the arguments *i and *(i-1) should be reversed:

6. Let pred be equal_to{} for the overloads with no parameter pred, and let E be
  1. (6.1) — bool(pred(*i, *(i - 1))) for the overloads in namespace std;
  2. (6.2) — bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1)))) for the overloads in namespace ranges.

This reversed order is consistent with the documentation for SGI STL unique_copy, although the docs for SGI STL unique show reversed arguments too, and the C++ standard doesn't match that.

A survey of known implementations shows that all three of libstdc++, libc++, and MSVC STL use the pred(*(i - 1), *i) order for all of std::unique, std::unique_copy, ranges::unique, and ranges::unique_copy. The range-v3 library did the same, and even the SGI STL did too (despite what its docs said). Only two implementations were found which match the spec and use a different argument order for unique and unique_copy, Casey Carter's (cmcstl2) and Fraser Gordon's.

In the absence of any known rationale for unique and unique_copy to differ, it seems sensible to make unique_copy more consistent with unique (and with the majority of implementations stretching back three decades).

Proposed resolution:

This wording is relative to N5008.

  1. Modify 26.7.9 [alg.unique] as indicated:

    6. Let pred be equal_to{} for the overloads with no parameter pred, and let E be
    1. (6.1) — bool(pred(*i, *(i - 1)), *i) for the overloads in namespace std;
    2. (6.2) — bool(invoke(comp, invoke(proj, *i), invoke(proj, *(i - 1)), invoke(proj, *i))) for the overloads in namespace ranges.