I have two implementations of the erase_all_if
function template.
template <typename Container, typename Pred>
typename Container::size_type erase_all_if(Container& c, Pred&& pred)
{
auto newend = std::remove_if(c.begin(), c.end(), std::forward<Pred>(pred));
auto ret = c.end() - newend;
c.erase(newend, c.end());
return ret;
}
template <typename Container, typename Pred>
auto erase_all_if(Container& c, Pred&& pred) {
typename Container::size_type removed = 0;
for (auto it = c.begin(); it != c.end();) {
if (pred(*it)) {
it = c.erase(it);
++removed;
}
else {
++it;
}
}
return removed;
}
The first works only for containers like std::vector
because it requires a random access iterator and the second one for all containers, but it is more inefficient for continuous containers and it is appropriate for node-based containers. How to disambiguate the two versions? The C++ standard I'm currently using is C++17.
You don't need SFINAE here. I would combine the two functions into one, and do
if constexpr (std::is_base_of_v<std::random_access_iterator_tag,
typename std::iterator_traits<decltype(c.begin())>::iterator_category>)
{
// ...
}
else
{
// ...
}
Or you can use tag dispatch: (note that iterator categories inherit from each other, so this handles fallback between categories nicely)
template <typename Container, typename Pred>
typename Container::size_type erase_all_if_low(std::random_access_iterator_tag, Container& c, Pred&& pred)
{
// ...
}
template <typename Container, typename Pred>
typename Container::size_type erase_all_if_low(std::forward_iterator_tag, Container& c, Pred&& pred)
{
// ...
}
template <typename Container, typename Pred>
auto erase_all_if(Container& c, Pred &&pred)
{
using category = typename std::iterator_traits<decltype(c.begin())>::iterator_category;
return erase_all_if_low(category{}, c, std::forward<Pred>(pred));
}