如何将一个 std::unique_ptr<> 从一个 STL 容器移动到另一个容器?

4

问题

我有一个模板容器MyContainer<std::unique_ptr<Foo>>,它有一个std::deque<T>和一个std::vector<T>成员。

在方法send_to_purgatory_if( predicate )中,如果谓词为真,我想查看m_taskdq中的所有项目并将项目从m_taskdq移动到m_purgatory

问题

我遇到了两个问题:

  • 如果我在循环中从m_taskdq中删除项,则我的迭代器it会被破坏
  • 如果我分两步进行移动(问题行1和2 - 到第二行时,我认为由it指向的std::unique_ptr<>是未定义的),则我担心std::unique_ptr<>的状态如何?

我应该如何修复这段代码?

    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;
    };

为什么要使用emplace_back?将项目附加到容器中并移动它是push_back(value_type&&)的工作。 emplace_back(args && ...)的主要重点是通过使用已经存在的构造函数直接向容器转发参数并进行构造。确实,如果只使用一个参数,则emplace_back会衰减为push_back,但在我看来这真的很不幸,只会导致混淆。更多信息请参见:https://dev59.com/E2855IYBdhLWcg3wg0nC#4306581,其中包括我的答案和lurscher的评论。 - Arzar
@ThomasPetit 因为我正在将一个 std::unique_ptr<> 从一个容器移动到另一个容器(如上面详细描述的那样)?你是告诉我在我的情况下,我可以/应该使用 push_back() 吗? - kfmfe04
我们接下来需要问的问题是,erase方法会将任务发送到天堂还是地狱。 - msmith81886
1个回答

13

这实际上是一个关于C++98的问题,但其中涉及了迷惑性的移动语义。首先要问的是如何在C++98中执行此操作:

std::deque::erase(iterator)返回一个引用被擦除元素之后的元素的iterator。所以先让它能够工作:

 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;
    }
  }

现在可以很容易地使用C++11移动语义使其正常工作:

 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;
    }
  }

taskdq中移动的unique_ptremplace_back之后变成了空的unique_ptr,然后在下一行被删除。没有伤害,也没有失误。

当有erase时,erase的返回值很好地增加了迭代器。而当没有erase时,则需要正常地增加迭代器。


1
非常感谢您对问题的精彩分解 - 我现在正在尝试它。 - kfmfe04

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接