The problem with `reject!` is that it's too ad-hoc: the more fundamental operation is to filter the elements of a stream. The input stream could be generated from any data structure (not necessarily an array), and the output stream could similarly be redirected into any data structure (not necessarily an array, let alone physically the same array).
Not clear what you're suggesting here. Can you implement reject! using your propose more general interface? If so, how? Is it more efficient than just creating a copy and then overwriting the original, like this?
ary = ary.reject {|x| x > 2}
If there's a language agnostic insight to be had here, I'd love to see it. Actually, even if it's language dependent, I'd like to see it.
auto first = vec.begin();
auto last = vec.end();
last = std::copy_if(first, last, first,
[](int x) { return x > 2; });
auto size = std::distance(first, last);
vec.resize(size);
(Ironically, in C++ this doesn't always work, because not all types are copyable or movable. But Ruby has no such problems.)
You can simplify that code a bit, since the contents of a `std::vector` are guaranteed to be contiguous in C++03 and later, and taking a pointer offset is therefore well-defined and correct.
const auto last{ std::copy_if(vec.cbegin(), vec.cend(), vec.begin(),
[](const auto x) { return x > 2; }) };
vec.resize(last - vec.cbegin());
`std::remove_if` only takes two iterator arguments, and it's too ad hoc in exactly the same way Ruby's `reject!` is too ad hoc: it's the result of specializing `std::copy_if` to the case when the zeroth and second iterator arguments agree.
remove_if is a convenience method; for full generality you can use remove_copy_if. I don't see how any of this allows you to achieve your idea of two independent containers backed by the same storage and thereby eliminate the extra step of calling resize.
Okay, but with copy_if, you need an extra resize step at the end, which is annoying in C++ and that Ruby users currently have the luxury of avoiding. And this seems fundamental. The reason that copy_if can't call resize for you is that it assumes nothing about the underlying container; indeed there may not even be one. You say this is a good thing, and there are certainly advantages, but it doesn't seem to solve this problem, unless I'm missing something. Or maybe you're saying that the need to call resize is worth it, since you get a more general interface for the price?
The resize step at the end is a defect of how C++'s collections work. Ideally, I'd like to be able to say that there are two collections, possibly stored in the same buffer so long as they don't overlap:
(0) A collection of elements to be processed (given by an InputIterator range).
(1) A collection of already processed elements (given by an OutputIterator range).
When the algorithm starts running, (1) is empty. With each iteration of the algorithm's loop, (0) becomes smaller and (1) becomes larger [maybe]. When the algorithm halts, (0) is empty.
Notice that, while both collections share the same underlying buffer, each has its own size. So there is no need to perform a `resize()` step at the end.