how to move an std::unique_ptr<> from one STL container to another?

為{幸葍}努か 提交于 2019-12-22 08:26:35

问题


Problem

I have a template container MyContainer<std::unique_ptr<Foo>> which has a std::deque<T> and a std::vector<T> member.

Inside method, send_to_purgatory_if( predicate ), I would like to look at all items in m_taskdq and move items from m_taskdq to m_purgatory, if the predicate evaluates to true.

Issues

I have two issues that I'm struggling with:

  • my iterator it gets trashed if I remove items from m_taskdq from inside the loop
  • I am worried about the state of the std::unique_ptr<> if I do the move in two steps (problem lines 1 and 2 - by line 2, I think the std::unique_ptr<> pointed to by it is undefined?)

How should I fix this code?

    template <typename T>
    class MyContainer
    {
      typedef std::function<bool(T&)>  PREDICATE;

      void send_to_purgatory_if( PREDICATE p )
      {
// bad code -------------------------------------
        for( auto it=m_taskdq.begin(); it!=m_taskdq.end(); ++it )
        {
          if ( p( *it ) )
          {
            m_purgatory.emplace_back( move( *it ));  // problem line 1
            m_taskdq.erase( it );                    // problem line 2
          }
        }
// end bad code ---------------------------------
      }

      std::deque<  T >  m_taskdq;                                                     
      std::vector< T >  m_purgatory;
    };

回答1:


This is really a C++98 question, with a red-herring concerning move semantics. The first thing to ask is how to do this in C++98:

std::deque::erase(iterator) returns an iterator that refers to the element after the one erased. So get that working first:

 void send_to_purgatory_if( PREDICATE p )
  {
    for( auto it=m_taskdq.begin(); it!=m_taskdq.end();)
    {
      if ( p( *it ) )
      {
        m_purgatory.emplace_back(*it);
        it = m_taskdq.erase(it);
      }
      else
        ++it;
    }
  }

And now it is easy to make it work with C++11 move semantics:

 void send_to_purgatory_if( PREDICATE p )
  {
    for( auto it=m_taskdq.begin(); it!=m_taskdq.end();)
    {
      if ( p( *it ) )
      {
        m_purgatory.emplace_back(std::move(*it));
        it = m_taskdq.erase(it);
      }
      else
        ++it;
    }
  }

The unique_ptr moved from in taskdq becomes a null unique_ptr after the emplace_back, and then it gets erased in the next line. No harm, no foul.

When there is an erase, the return from the erase does a good job at incrementing the iterator. And when there is no erase, a normal iterator increment is in order.



来源:https://stackoverflow.com/questions/9661056/how-to-move-an-stdunique-ptr-from-one-stl-container-to-another

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!