Search code examples
c++stlc++17erase-remove-idiom

Why is a convenience helper for the erase-remove-idiom not provided by the standard?


Removing items from a collection in the STL requires a technique used so often that is has become an idiom: the erase-remove-idiom

One of the most common usages of this idiom is to remove an item of type T from a vector<T>

std::vector<Widget> widget_collection;
Widget widget;

widget_collection.erase(
    std::remove(widget_collection.begin(), widget_collection.end(), widget), 
    widget_collection.end());

This is obviously very verbose, and violates the DRY principle - the vector in question is required 4 times there.

So my question is why does the standard not provide a convenience helper?

Something like

widget_collection.erase_remove(widget);

or

std::erase_remove(widget_collection, widget);

This could obviously be extended to

widget_collection.erase_remove_if(widget, pred);

etc...


Solution

  • This issue is covered by proposal N4009: Uniform Container Erasure which says:

    This is a proposal to add erase_if(container, pred), making it easier to eliminate unwanted elements correctly and efficiently.

    [...]

    It's surprisingly difficult to eliminate unwanted elements from a container, given a predicate that distinguishes "bad" elements from "good" elements.

    One of the STL's major strengths is that all of its containers have similar interfaces - they have many functions in common and they follow the same conventions. When container interfaces vary, fundamental differences between their data structures are responsible. Even those differences can often be ignored, thanks to the STL's container-iterator-algorithm design.

    and also notes:

    The correct response is to use the erase-remove idiom, which is non-obvious and must be taught instead of discovered (it's called an "idiom" for a reason).

    the latest version N4273: Uniform Container Erasure (Revision 2) looks like it was adopted. It is part of Extensions for Library Fundamentals V2 . Also see the cppreference section for C++ standard libraries extensions, version 2.

    The head revision(version 6.0.0) of gcc available on Wandbox has an implementation of this header (see it live):

    #include <experimental/vector>
    #include <iostream>
    
    int main()
    {
        std::vector<int> v1 = {1,2,3,4,5,6} ;
    
        std::experimental::erase_if( v1, [] (const int &x ) { return x < 4; } ) ;
    
        for( const auto & v : v1 )
        {
            std::cout << v << ", " ;
        }
        std::cout << "\n" ;
    }
    

    This code also work on webcompiler which seems to confirm T.C.'s suggestion that this also shipped with MSVC 2015.