Przejdź do głównej zawartości

std::ranges::unique() algorithm

// (1)
constexpr ranges::subrange<I>
unique( I first, S last, C comp = {}, Proj proj = {} );

// (2)
constexpr ranges::borrowed_subrange_t<R>
unique( R&& r, C comp = {}, Proj proj = {} );

The type of arguments are generic and have the following constraints:

  • I - std::permutable
  • S - std::sentinel_for<I>
  • R - std::ranges::forward_range
  • C:
    • (1) - std::indirect_equivalence_relation<std::projected<I, Proj>>
    • (2) - std::indirect_equivalence_relation<std::projected<ranges::iterator_t<R>, Proj>>
  • Proj - (none)

The Proj and C template argument have the following default types std::identity, ranges::equal_to for all overloads.

Additionally, each overload has the following constraints:

  • (2) - std::permutable<ranges::iterator_t<R>>
  • (1) Eliminates all except the first element from every consecutive group of equivalent elements from the range [first; last) and returns a subrange [ret; last), where ret is a past-the-end iterator for the new end of the range.

    Two consecutive elements *(i - 1) and *i are considered equivalent if std::invoke(comp, std::invoke(proj, *(i - 1)), std::invoke(proj, *i)) == true, where i is an iterator in the range [first + 1; last).

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

The function-like entities described on this page are niebloids.

Parameters

first
last

The range of elements to process.

r

The range of elements to process.

comp

The binary predicate to compare the projected elements with.

proj

The projection to apply to the elements.

Return value

Returns

{
ret,
last
}

Where ret is a past-the-end iterator for the new end of the range.

Complexity

For nonempty ranges, exactly ranges::distance(first, last) - 1 applications of the corresponding predicate comp and no more that twice as many applications of any projection proj.

Exceptions

(none)

Possible implementation

unique(1) and unique(2)
struct unique_fn
{
template<std::permutable I, std::sentinel_for<I> S, class Proj = std::identity,
std::indirect_equivalence_relation<std::projected<I, Proj>>
C = ranges::equal_to>
constexpr ranges::subrange<I>
operator()(I first, S last, C comp = {}, Proj proj = {}) const
{
first = ranges::adjacent_find(first, last, comp, proj);
if (first == last)
return {first, first};
auto i {first};
++first;
while (++first != last)
if (!std::invoke(comp, std::invoke(proj, *i), std::invoke(proj, *first)))
*++i = ranges::iter_move(first);
return {++i, first};
}

template<ranges::forward_range R, class Proj = std::identity,
std::indirect_equivalence_relation<std::projected<ranges::iterator_t<R>, Proj>>
C = ranges::equal_to>
requires std::permutable<ranges::iterator_t<R>>
constexpr ranges::borrowed_subrange_t<R>
operator()(R&& r, C comp = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r),
std::move(comp), std::move(proj));
}
};

inline constexpr unique_fn unique {};

Notes

Removing is done by shifting (by means of move assignment) the elements in the range in such a way that the elements that are not to be removed appear in the beginning of the range. Relative order of the elements that remain is preserved and the physical size of the container is unchanged.

Iterators in [ret; last) (if any) are still dereferenceable, but the elements themselves have unspecified values (as per MoveAssignable post-condition).

A call to ranges::unique is sometimes followed by a call to a container’s erase member function, which erases the unspecified values and reduces the physical size of the container to match its new logical size. These two invocations together model the Erase–remove idiom.

Examples

Main.cpp
#include <algorithm>
#include <cmath>
#include <complex>
#include <iostream>
#include <vector>

struct id {
int i;
explicit id(int i) : i {i} {}
};

void print(id i, const auto& v)
{
std::cout << i.i << ") ";
std::ranges::for_each(v, [](auto const& e) { std::cout << e << ' '; });
std::cout << '\n';
}

int main()
{
// a vector containing several duplicated elements
std::vector<int> v {1, 2, 1, 1, 3, 3, 3, 4, 5, 4};

print(id {1}, v);

// remove consecutive (adjacent) duplicates
const auto ret = std::ranges::unique(v);
// v now holds {1 2 1 3 4 5 4 x x x}, where 'x' is indeterminate
v.erase(ret.begin(), ret.end());
print(id {2}, v);

// sort followed by unique, to remove all duplicates
std::ranges::sort(v); // {1 1 2 3 4 4 5}
print(id {3}, v);

const auto [first, last] = std::ranges::unique(v.begin(), v.end());
// v now holds {1 2 3 4 5 x x}, where 'x' is indeterminate
v.erase(first, last);
print(id {4}, v);

// unique with custom comparison and projection
std::vector<std::complex<int>> vc { {1, 1}, {-1, 2}, {-2, 3}, {2, 4}, {-3, 5} };
print(id {5}, vc);

const auto ret2 = std::ranges::unique(vc,
// consider two complex nums equal if their real parts are equal by module:
[](int x, int y) { return std::abs(x) == std::abs(y); }, // comp
[](std::complex<int> z) { return z.real(); } // proj
);
vc.erase(ret2.begin(), ret2.end());
print(id {6}, vc);
}
Possible Output
1) 1 2 1 1 3 3 3 4 5 4
2) 1 2 1 3 4 5 4
3) 1 1 2 3 4 4 5
4) 1 2 3 4 5
5) (1,1) (-1,2) (-2,3) (2,4) (-3,5)
6) (1,1) (-2,3) (-3,5)
This article originates from this CppReference page. It was likely altered for improvements or editors' preference. Click "Edit this page" to see all changes made to this document.
Hover to see the original license.

std::ranges::unique() algorithm

// (1)
constexpr ranges::subrange<I>
unique( I first, S last, C comp = {}, Proj proj = {} );

// (2)
constexpr ranges::borrowed_subrange_t<R>
unique( R&& r, C comp = {}, Proj proj = {} );

The type of arguments are generic and have the following constraints:

  • I - std::permutable
  • S - std::sentinel_for<I>
  • R - std::ranges::forward_range
  • C:
    • (1) - std::indirect_equivalence_relation<std::projected<I, Proj>>
    • (2) - std::indirect_equivalence_relation<std::projected<ranges::iterator_t<R>, Proj>>
  • Proj - (none)

The Proj and C template argument have the following default types std::identity, ranges::equal_to for all overloads.

Additionally, each overload has the following constraints:

  • (2) - std::permutable<ranges::iterator_t<R>>
  • (1) Eliminates all except the first element from every consecutive group of equivalent elements from the range [first; last) and returns a subrange [ret; last), where ret is a past-the-end iterator for the new end of the range.

    Two consecutive elements *(i - 1) and *i are considered equivalent if std::invoke(comp, std::invoke(proj, *(i - 1)), std::invoke(proj, *i)) == true, where i is an iterator in the range [first + 1; last).

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

The function-like entities described on this page are niebloids.

Parameters

first
last

The range of elements to process.

r

The range of elements to process.

comp

The binary predicate to compare the projected elements with.

proj

The projection to apply to the elements.

Return value

Returns

{
ret,
last
}

Where ret is a past-the-end iterator for the new end of the range.

Complexity

For nonempty ranges, exactly ranges::distance(first, last) - 1 applications of the corresponding predicate comp and no more that twice as many applications of any projection proj.

Exceptions

(none)

Possible implementation

unique(1) and unique(2)
struct unique_fn
{
template<std::permutable I, std::sentinel_for<I> S, class Proj = std::identity,
std::indirect_equivalence_relation<std::projected<I, Proj>>
C = ranges::equal_to>
constexpr ranges::subrange<I>
operator()(I first, S last, C comp = {}, Proj proj = {}) const
{
first = ranges::adjacent_find(first, last, comp, proj);
if (first == last)
return {first, first};
auto i {first};
++first;
while (++first != last)
if (!std::invoke(comp, std::invoke(proj, *i), std::invoke(proj, *first)))
*++i = ranges::iter_move(first);
return {++i, first};
}

template<ranges::forward_range R, class Proj = std::identity,
std::indirect_equivalence_relation<std::projected<ranges::iterator_t<R>, Proj>>
C = ranges::equal_to>
requires std::permutable<ranges::iterator_t<R>>
constexpr ranges::borrowed_subrange_t<R>
operator()(R&& r, C comp = {}, Proj proj = {}) const
{
return (*this)(ranges::begin(r), ranges::end(r),
std::move(comp), std::move(proj));
}
};

inline constexpr unique_fn unique {};

Notes

Removing is done by shifting (by means of move assignment) the elements in the range in such a way that the elements that are not to be removed appear in the beginning of the range. Relative order of the elements that remain is preserved and the physical size of the container is unchanged.

Iterators in [ret; last) (if any) are still dereferenceable, but the elements themselves have unspecified values (as per MoveAssignable post-condition).

A call to ranges::unique is sometimes followed by a call to a container’s erase member function, which erases the unspecified values and reduces the physical size of the container to match its new logical size. These two invocations together model the Erase–remove idiom.

Examples

Main.cpp
#include <algorithm>
#include <cmath>
#include <complex>
#include <iostream>
#include <vector>

struct id {
int i;
explicit id(int i) : i {i} {}
};

void print(id i, const auto& v)
{
std::cout << i.i << ") ";
std::ranges::for_each(v, [](auto const& e) { std::cout << e << ' '; });
std::cout << '\n';
}

int main()
{
// a vector containing several duplicated elements
std::vector<int> v {1, 2, 1, 1, 3, 3, 3, 4, 5, 4};

print(id {1}, v);

// remove consecutive (adjacent) duplicates
const auto ret = std::ranges::unique(v);
// v now holds {1 2 1 3 4 5 4 x x x}, where 'x' is indeterminate
v.erase(ret.begin(), ret.end());
print(id {2}, v);

// sort followed by unique, to remove all duplicates
std::ranges::sort(v); // {1 1 2 3 4 4 5}
print(id {3}, v);

const auto [first, last] = std::ranges::unique(v.begin(), v.end());
// v now holds {1 2 3 4 5 x x}, where 'x' is indeterminate
v.erase(first, last);
print(id {4}, v);

// unique with custom comparison and projection
std::vector<std::complex<int>> vc { {1, 1}, {-1, 2}, {-2, 3}, {2, 4}, {-3, 5} };
print(id {5}, vc);

const auto ret2 = std::ranges::unique(vc,
// consider two complex nums equal if their real parts are equal by module:
[](int x, int y) { return std::abs(x) == std::abs(y); }, // comp
[](std::complex<int> z) { return z.real(); } // proj
);
vc.erase(ret2.begin(), ret2.end());
print(id {6}, vc);
}
Possible Output
1) 1 2 1 1 3 3 3 4 5 4
2) 1 2 1 3 4 5 4
3) 1 1 2 3 4 4 5
4) 1 2 3 4 5
5) (1,1) (-1,2) (-2,3) (2,4) (-3,5)
6) (1,1) (-2,3) (-3,5)
This article originates from this CppReference page. It was likely altered for improvements or editors' preference. Click "Edit this page" to see all changes made to this document.
Hover to see the original license.