Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to erase a std::map cell by value using STL algorithms and functors?

I coded a small test entity manager in C++. I have a method called 'removeEntityByName' using a simple erase. Until here there is none problem. Now I have another method called 'removeEntityByPtr' which must delete by value. Here's the code which works :

void EntityManager::removeEntityByPtr(Entity *pEntity)
{
    std::map<std::string, Entity*>::iterator It = this->m_EntityList.begin();

    for (; It != this->m_EntityList.end();) {
        if ((*It).second == pEntity) {
            this->m_EntityList.erase(It++);
        } else {
            ++It;
        }
    }
}

But it's not very beautiful. So, I wondered if I could find a more elegant way using STL algorithms and functors to do the job correctly (I know there is no problem with std::vector but with std::map I'm not sure it works). So here's the different functors I tried to use:

template <typename T>
struct DeleteFunctor
{
    DeleteFunctor(T *pointer)
        :   m_Pointer(pointer)
    {

    }

    bool operator()(std::string, Entity *pEntity)
    {
        if (pEntity == this->m_Pointer) {
            delete(this->m_Pointer);
            return (true);
        }
        return (false);
    }

    T *m_Pointer;
};

template <typename T>
struct DeleteFunctor
{
    DeleteFunctor(T *pointer)
        :   m_Pointer(pointer)
    {

    }

    bool operator()(std::pair<std::string, Entity*> const &cell)
    {
        if (cell.second == this->m_Pointer) {
            delete(this->m_Pointer);
            return (true);
        }
        return (false);
    }

    T *m_Pointer;
};

And the STL algorithms :

this->m_EntityList.erase(std::remove(this->m_EntityList.begin(), this->m_EntityList.end(), DeleteFunctor<Entity>(pEntity)), this->m_EntityList.end());

I want to precise that the syntax is correct with Visual Studio. But the compilation failed.

Here's the compilation errors :

error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::pair<_Ty1,_Ty2>'
 with
[
   _Ty1=const std::string,
   _Ty2=Entity *
]
c:\program files (x86)\microsoft visual studio 11.0\vc\include\exception(507): peut être 'bool std::operator ==(const std::exception_ptr &,const std::exception_ptr &)'
c:\program files (x86)\microsoft visual studio 11.0\vc\include\exception(512): ou       'bool std::operator ==(std::nullptr_t,const std::exception_ptr &)'
c:\program files (x86)\microsoft visual studio 11.0\vc\include\exception(517): ou       'bool std::operator ==(const std::exception_ptr &,std::nullptr_t)'
c:\program files (x86)\microsoft visual studio 11.0\vc\include\system_error(426): ou       'bool std::operator ==(const std::error_code &,const std::error_condition &) throw()'
c:\program files (x86)\microsoft visual studio 11.0\vc\include\system_error(434): ou       'bool std::operator ==(const std::error_condition &,const std::error_code &) throw()'
when attempting matching of the argument list '(std::pair<_Ty1,_Ty2>, const DeleteFunctor)'
with
[
  _Ty1=const std::string,
  _Ty2=Entity *
]
c:\program files (x86)\microsoft visual studio 11.0\vc\include\algorithm(1788) : see the reference of the model function '_FwdIt std::_Remove<std::_Tree_unchecked_iterator<_Mytree>,_Ty>(_FwdIt,_FwdIt,const _Ty &)' en cours de compilation
with
[     _FwdIt=std::_Tree_unchecked_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<const std::string,Entity *>>>>,
 _Mytree=std::_Tree_val<std::_Tree_simple_types<std::pair<const std::string,Entity *>>>,
 _Ty=DeleteFunctor
]
c:\users\volodia\desktop\testmanager\testmanager\entitymanager.cpp(76) : voir la référence à l'instanciation de la fonction modèle '_FwdIt std::remove<std::_Tree_iterator<_Mytree>,DeleteFunctor>(_FwdIt,_FwdIt,const _Ty &)' en cours de compilation
with
[
 _FwdIt=std::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<const std::string,Entity *>>>>,
  _Mytree=std::_Tree_val<std::_Tree_simple_types<std::pair<const std::string,Entity *>>>,
  _Ty=DeleteFunctor
]

I tried several code combinations without any success. Does anyone can help me please ? Thanks a lot in advance foir your help.

like image 203
user1364743 Avatar asked Jan 31 '26 10:01

user1364743


1 Answers

Many algorithms, including std::remove work by moving elements around in a container. For example, std::remove swaps unwanted elements to the back, and the wanted to the front.

This is not allowed in associative containers like std::map and std::set, because the order in wich elements are stored is fixed, so you can't use those algorithms on the containers.

Having said that, the first approach you made is correct, although you could separate the algorithm from the logic:

template <class Container, class Pred>
void erase_if(Container& cont, Pred pred) {
  for (auto first = begin(cont), last = end(cont); first != last;)
  {
    if (pred(*first))
      first = cont.erase(first);
    else
      ++first;
  }
}

And then in your code (C++14 style):

void EntityManager::removeEntityByPtr(Entity *pEntity)
{
  erase_if(this->m_EntityList, [=](auto const& keyValue) { 
    return keyValue.second == pEntity;
  }
}

In C++11 you'll have to specify the type of the lambda function parameter, which is std::pair<std::string const, Entity*>.
In C++03 you have to roll an extra functor, specify the iterator type inside the erase_if for loop like you did in your example, and call the begin and end member functions of the container instead of the free functions introduced in C++11

like image 92
Arne Mertz Avatar answered Feb 02 '26 02:02

Arne Mertz



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!