Does C++ standard library provide more compact and generalized version of the erase–remove idiom?
Asked Answered
C

1

21

We can erase one element/ entry from a container by the popular erase–remove idiom. However, many of us would have encountered some problems while applying this idiom:

  • one can easily get into the pitfall of typos like

    c.erase(std::remove_if(c.begin(), c.end(), pred));
    //                                             , c.end() //---> missing here
    

    or

    c.erase((std::remove_if(c.begin(), c.end(), pred), c.end()))
    //      ^^                                               ^^
    // extra () makes it pass only c.end() to the c.erase
    
  • It even follows the wrong semantics for containers like std::list by not selecting its own member std::list::remove_if() for the idiom.
  • Thirdly, using std::remove_if does not work for associative containers.

Do we have anything generalized and less typo-prone than std::erase-std::remove_if or something like std::erase_if within the scope of , or will there be such a utility in ?

Ceceliacecil answered 2/7, 2019 at 20:15 Comment(0)
C
31

Not in the scope of , but onwards!

Yes. The proposal of consistent container erasure has been mentioned in n4009 paper and finally adopted in C++20 standard as std::erase_if which is a non-member function for each containers.

This ensures a uniform container erasure semantics for std::basic_string and all standard containers, except std::array(as it has the fixed-size).

This means that the boilerplate code

container.erase(
    std::remove_if(
        container.begin(), container.end(),
        [](const auto& element) ->bool { return /* condition */; }),
    vec.end());

will simply melt down to a generalized form of

std::erase_if(container, [](const auto& element) ->bool { return /* condition */; });

Secondly, this uniform syntax selects the proper semantics for each container. This means


In addition to that, the standard also added std::erase for sequence containers of the form

std::erase(container, value_to_be_removed);
Ceceliacecil answered 2/7, 2019 at 20:16 Comment(7)
How do I extend this for my own containers? Will my own erase_if(...) be found by ADL?Unreason
@Unreason if I understood correctly, defining a non-member erase_if(...) for the own container wouldn't be enough? maybe putting the scope of std:: will be adding to namespace std. But later part I am not sure.Ceceliacecil
Indeed, from the paper, this utility is specified for each standard container and doesn't use ADL. As adding a custom overload to std is not allowed, the way to customize this for your own containers would be to define your own separate erase_if in your own namespace, which could forward on to std::erase_if or your own containers'.Unreason
@Unreason No ADL means that an unqualified call, like { using std::erase_if; erase_if(...); }, won't help to find ones own non-member erase_if, right?Counterbalance
@TedLyngmo You could write that, and it would find your own erase_if (provided it can be found by ADL, such as if it is defined in the same namespace as your container).Unreason
Whatever for do you force the lambdas to have return-type bool?Glim
@Glim It is a matter of taste. I would say. being explicit retin [ captures ] ( params ) -> ret { body } (about lambdas), not a bad practice, IMHO.Ceceliacecil

© 2022 - 2024 — McMap. All rights reserved.