21

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.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • I can't see `std::ranges::remove` documented anywhere on cppreference, is it part of the standard? Looks like boost ranges has remove but not c++? – Alan Birtles Oct 10 '20 at 19:35
  • @AlanBirtles Yes, it's [here](https://eel.is/c++draft/alg.remove). It's listed [here](https://en.cppreference.com/w/cpp/algorithm/ranges), on cppreference as well, but there isn't a specific page for it yet. – cigien Oct 10 '20 at 19:44
  • @AlanBirtles I switched to `ranges::unique` anyway, which is also in c++20. – cigien Oct 10 '20 at 20:10

3 Answers3

13

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));
cigien
  • 57,834
  • 11
  • 73
  • 112
Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
6

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).

ivan.ukr
  • 2,853
  • 1
  • 23
  • 41
  • 1
    This is nice, but it doesn't really answer the question. This only works if I remove elements using an equivalent of `remove`. It doesn't work if I want to remove elements with an equivalent of `unique`. I've updated the question. – cigien Oct 10 '20 at 20:06
6

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);
metalfox
  • 6,301
  • 1
  • 21
  • 43
  • I'm accepting this answer, this is much cleaner. It should be the new idiom IMO :) – cigien Oct 18 '20 at 14:45
  • 2
    @康桓瑋 No, it's fine. You just need to include ``. – cigien Oct 20 '20 at 03:16
  • [But should we consider this as a gcc library bug?](https://godbolt.org/z/vex9s9) – 康桓瑋 Oct 20 '20 at 09:33
  • 1
    @康桓瑋 I don't think so. `std::ranges::subrange` is declared in the `` header. I assume that the GCC team decided not to include all of `` in ``. Maybe they defined the `PairLike` protocol for `subrange` in a header that is not accessible by ``, but I don't see why that wouldn't be standard-compliant. – metalfox Oct 20 '20 at 18:05
  • 1
    @康桓瑋 Part of the `subrange` structured bindings stuff is defined in the `` header [itself](https://github.com/gcc-mirror/gcc/blob/e2e04288542667307df925f7d0a4b0fa2030f741/libstdc%2B%2B-v3/include/std/ranges#L3323), while `` includes [``](https://github.com/gcc-mirror/gcc/blob/e2e04288542667307df925f7d0a4b0fa2030f741/libstdc%2B%2B-v3/include/std/algorithm#L64), which includes [``](https://github.com/gcc-mirror/gcc/blob/e2e04288542667307df925f7d0a4b0fa2030f741/libstdc%2B%2B-v3/include/bits/ranges_algo.h#L36) where `subrange` is defined. – metalfox Oct 21 '20 at 06:04
  • @metalfox, Yep I see. IMO, put `subrange`'s structured bindings stuff together with its definition should be convenient for users, since `std::ranges::remove/unique` return a `subrange`, for a user like me usually want to structurally bind it without extra including ``, and the compiler error message `cannot decompose inaccessible member 'std::ranges::subrange` also confused me. – 康桓瑋 Oct 21 '20 at 06:24
  • @康桓瑋 IMO it would make sense to have these `tuple`-related specializations close to the `get` functions, which are [already](https://github.com/gcc-mirror/gcc/blob/e2e04288542667307df925f7d0a4b0fa2030f741/libstdc%2B%2B-v3/include/bits/ranges_util.h#L377) in the `` header. You might file a bug report requesting that. Even though it's not a bug, I'd say that, if there are no technical objections, they'll agree to move this stuff. – metalfox Oct 21 '20 at 07:29
  • 2
    @metalfox Already filed a [report](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97512) for this. Thank you for your advice. – 康桓瑋 Oct 21 '20 at 13:41
  • 1
    @康桓瑋 Wow, you got it fixed quickly! Thanks for the report. – metalfox Oct 21 '20 at 14:44