It's fun. At first, I tried to code the correct IMO approach using a custom iterator that simply skips elements that satisfy the predicate. It turned out to be quite complicated, at least writing on a mobile phone when I do this.
Basically, this should result in code similar to what you can find in the Eric Niebler v3 ranges .
But there is a simpler, more direct approach that you are trying to use above. The problem with your non-working solution is that it changes the values pointed to by the (remaining sorted) iterators when assigned in the last for loop. This problem can be fixed by copying, as in my code:
int main(int, char **) { vector<int> input {1,2,3,4,5,6,7,8,9}; vector<reference_wrapper<int>> filtered{begin(input), end(input)}; filtered.erase(remove_if(begin(filtered), end(filtered), [](auto e) {return e%2==0;}), end(filtered)); vector<int> sorted{begin(filtered), end(filtered)};
Live example here. Forgot in fork before modification, sorry.
(Instead of using copies for types that use data allocated by the move heap, you should probably use it. Although I'm not sure if it can be assigned to a moved object.)
Using ... a rather strange ... wrapper std::reference_wrapper instead of std::reference_wrapper makes it possible to filter without sorting , to use a vector with (copied or moved) elements of type value:
template <class T> class copyable_ref { public: copyable_ref(T& ref) noexcept : _ptr(std::addressof(ref)), _copied(false) {} copyable_ref(T&&) = delete; copyable_ref(const copyable_ref& x) noexcept : _ptr (new int(*x._ptr)), _copied (true) { } ~copyable_ref() { if (_copied) { delete _ptr; } } copyable_ref& operator=(const copyable_ref& x) noexcept { *_ptr = *x._ptr; } operator T& () const noexcept { return *_ptr; } T& get() const noexcept { return *_ptr; } private: T* _ptr; bool _copied; };
During construction, this class stores a pointer to its argument, which also changes when the copy destination operator is used. But when an instance is created by the copy, then a bunch of the selected copy of the reference (other) value is created. Thus, you can replace the two reference values with code similar to
Value a, b; copyable_ref<Value> ref_a{a}, ref_b{b}; copyable_ref<Value> temp{ref_a}; ref_a = ref_b; ref_b = temp;
This was necessary because std::sort does not seem to use swap (found through ADL or std::swap ), but code equivalent to the one above.
Now you can sort the filtered “view” by filling the vector with instances ( not copied by the designed ones ) of the strange wrapper class and sorting this vector. As the result in the example shows, no more than one heap is allocated a copy of the value type. Apart from the required size for pointers inside the shell, this class allows filtering sorting with a constant spatial cost:
vector<int> input {1,2,3,4,5,6,7,8,9}; vector<copyable_ref<int>> sorted; sorted.reserve(input.size()); for (auto & e : input) { if (e % 2 != 0) { sorted.emplace_back(e); } } sort(begin(sorted), end(sorted), greater<int>{}); copy(begin(input), end(input), ostream_iterator<int>{cout, ", "}); cout << endl;
Finally, although this works pretty well, I probably won’t use this in production code. I was especially surprised that std::sort did not use my own swap implementation, which led to this adventurous copy constructor.
You cannot generalize your code to work with sets and maps: they are sorted by design, and this requires that the fixed order function properly. And unordered options, well, unordered and therefore cannot maintain order. But you can always (unless you modify the container) use std::reference_wrapper inside the vector to provide a sorted “view” of your data.