Search code examples
c++dictionaryvectorboosterase

C++ boost::range::remove_erase does not work on std::map?


The code snippet

std::map<int, int> m = { { 1, 2 }, { 3, 4 } };
boost::range::remove_erase_if(
    m,
    [](const auto& it)
    {
        return it.first == 1;
    });

produces the error

12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133

\include\xmemory(1986,1): error C2679: binary '=': no operator found which takes a right-hand operand of type 'std::pair<const int,int>' (or there is no acceptable conversion)
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133\include\utility(269,11): message : could be 'std::pair<const int,int> &std::pair<const int,int>::operator =(volatile const std::pair<const int,int> &)'
12>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.29.30133\include\xmemory(1986,1): message : while trying to match the argument list '(std::pair<const int,int>, std::pair<const int,int>)'

I expected remove_erase_if to remove the first pair {1, 2} from the map.

remove_erase_if behaves as expected when I try it with a vector instead of a map:

std::vector<int> v = { 1, 2, 3, 4 };
boost::range::remove_erase_if(
    v,
    [](const auto& it)
    {
        return it == 1;
    });

Here v contains {2, 3, 4} after execution. What do I have to adjust, to use remove_erase_if with a map?


Solution

  • In your case boost::remove_erase_if will do the same as

    m.erase(std::remove_if(m.begin(), m.end(), [](const auto& it) { return it.first == 1; }), m.end());
    

    which doesn't work well on std::maps.

    If you can't use C++20 std::erase_if(std::map), make your own. When you later upgrade to C++20, you can replace the dnx namespace with std in your code that uses dnx::erase_if.

    namespace dnx {
    template <class Key, class T, class Compare, class Alloc, class Pred>
    typename std::map<Key, T, Compare, Alloc>::size_type erase_if(
        std::map<Key, T, Compare, Alloc>& c, Pred pred) {
        auto old_size = c.size();
        for (auto i = c.begin(), last = c.end(); i != last;) {
            if (pred(*i)) {
                i = c.erase(i);
            } else {
                ++i;
            }
        }
        return old_size - c.size();
    }
    }  // namespace dnx
    
    int main() {
        std::map<int, int> m = {{1, 2}, {3, 4}};
    
        // now works:
        dnx::erase_if(m, [](const auto& it) { return it.first == 1; });
    }