How does the erase-remove idiom work with ranges/constrained algorithms?

3k views Asked by At

I'm trying to use a c++20 constrained algorithm for the erase-remove idiom:

std::vector<int> v;
v.erase(std::unique(std::begin(v), std::end(v)), std::end(v));

but when I do a simple transformation:

v.erase(std::ranges::unique(v), std::end(v));

I get an error that the arguments to erase don't match:

error: no matching function for call to 'std::vector<int>::erase(std::ranges::borrowed_subrange_t<std::vector<int>&>, std::vector<int>::iterator)'

A similar error is produced if the second argument is std::ranges::end(v).

How can I get this to work?


The question originally used remove instead of unique, but there is an overloaded std::erase for all containers that makes that particular use case less motivating.

3

There are 3 answers

9
metalfox On BEST ANSWER

Another option would be decomposing the subrange returned by std::ranges::remove/unique, and use those iterators:

auto [Beg, End] = std::ranges::remove(v, 42);
v.erase(Beg, End);
1
ivan.ukr On

It doesn't work since std::ranges::remove() returns not iterator but range. But even if you try v.erase(std::ranges::remove(...)) it will not work, because vector does not have erase() overload which takes range as parameter.

Instead, take a look at std::erase() (defined in <vector>). What you need is probably just std::erase(v, 42).

0
Alan Birtles On

std::ranges::unique (and std::ranges::remove) returns a sub range from the first removed element to the end of the container so you need to use std::begin before passing to std::vector::erase:

v.erase(std::ranges::begin(std::ranges::remove(v, 42)), std::end(v));
v.erase(std::ranges::begin(std::ranges::unique(v)), std::end(v));