【问题标题】:Insert into an STL queue using std::copy使用 std::copy 插入 STL 队列
【发布时间】:2010-12-15 23:05:34
【问题描述】:

我想使用std::copy 将元素插入到这样的队列中:

vector<int> v;
v.push_back( 1 );
v.push_back( 2 );

queue<int> q;

copy( v.begin(), v.end(), insert_iterator< queue<int> >( q, q.front() ) );

但是这个编译失败,抱怨begin不是std::queue的成员。

注意:我也用std::inserter 尝试过——这也失败了,这一次说'reference' 不是'std::queue' 的成员。 std::back_inserterstd::back_insert_iterator 也会失败并出现相同的错误。

我是否遗漏了一些明显的东西,或者 insert_iterators 只是不适用于队列?

【问题讨论】:

  • 虽然你得到的答案很好,但我个人会避免使用 std::queue 和任何其他残缺的容器适配器。
  • 是的,sbi 和 Naveen 建议使用双端队列是一个不错的选择。

标签: c++ stl copy queue insert-iterator


【解决方案1】:

std::queue 不是 STL 意义上的容器,它是一个功能非常有限的容器适配器。对于您似乎需要std::vectorstd::deque(“双端队列,这是一个“真正的容器”),似乎是正确的选择。

【讨论】:

  • 或者使用for循环推入队列。不过,我所做的似乎并没有什么不合理的,不是吗?
  • 不是,不是。请参阅 Frank 的回答,了解如何实现您想要的。
  • 不,它没有(功能上),但不幸的是,您将不得不滚动自己的插入器。
【解决方案2】:

队列不允许遍历其元素。

来自SGI STL Docs

队列是一个适配器,它提供了一个 容器的受限子集 功能队列是“先入 先出”(FIFO)数据结构。1 也就是说,元素被添加到 队列的后面,可能会被删除 从前面; Q.front() 是 添加到队列中的元素 至少最近。队列不允许 迭代其元素。 [2]

可以完成这项工作,但您不能使用insert_iterator。您必须编写类似 queue_inserter 的内容来提供迭代器接口。

更新我情不自禁地决定尝试实现您需要的迭代器。结果如下:

template< typename T, typename U >
class queue_inserter {
    queue<T, U> &qu;  
public:
    queue_inserter(queue<T,U> &q) : qu(q) { }
    queue_inserter<T,U> operator ++ (int) { return *this; }
    queue_inserter<T,U> operator * () { return *this; }
    void operator = (const T &val) { qu.push(val); }
};

template< typename T, typename U >
queue_inserter<T,U> make_queue_inserter(queue<T,U> &q) {
    return queue_inserter<T,U>(q);
}    

这非常适合这样的功能:

template<typename II, typename OI>
void mycopy(II b, II e, OI oi) {
    while (b != e) { *oi++ = *b++; }
}

但它不适用于 STL 副本,因为 STL 很愚蠢。

【讨论】:

  • Andy 没有尝试遍历队列,而是追加到队列中。
  • 否则你的答案是合理的。 :) +1
  • 为什么不实现queue_inserter对应back_insert_iterator接口呢? std::copy 不是“愚蠢”(恕我直言!),它只需要一个不是繁重接口的输出迭代器。
  • 您的 queue_inserter 不可分配,但除此之外您没有iterator 派生,您只需提供iterator_traits 的特化,如果您希望能够将迭代器与标准算法一起使用。
  • 顺便说一句,std::copy 特别是通常需要了解更多关于迭代器的信息,因此它可以选择一种最佳方式来做到这一点:例如指针 + POD 可以被 memmoved。
【解决方案3】:

您需要的是一个push_inserter(即在队列中执行pushes 的插入器)。据我所知,STL 中没有这样的迭代器。我通常会遗憾地退回到旧的 for 循环。

如果你有勇气,你可以推出自己的迭代器,按照以下思路:

template <typename Container>
class push_insert_iterator
{
  public:
    typedef Container                      container_type;
    typedef typename Container::value_type value_type;

    explicit push_insert_iterator(container_type & c)
        : container(c)
    {}    // construct with container

    push_insert_iterator<container_type> & operator=(const value_type & v)
    {
        //push value into the queue
        container.push(v);
        return (*this);
    }

    push_insert_iterator<container_type> & operator*()
    {
        return (*this);
    }

    push_insert_iterator<container_type> & operator++()
    {
        // Do nothing
        return (*this);
    }

    push_insert_iterator<container_type> operator++(int)
    {
        // Do nothing
        return (*this);
    }

  protected:
    container_type & container;    // reference to container
};

template <typename Container>
inline push_insert_iterator<Container> push_inserter(Container & c)
{
    return push_insert_iterator<Container>(c);
}

这只是一个草稿,但你明白了。适用于任何具有push 方法(例如queuestack)的容器(或者,容器适配器)。

【讨论】:

  • 它在 STL 中称为“后插入器”,您可以从back_inserter 获得一个。但是,这不适用于std::queue
  • 我知道什么是 back_insert_iterator:一个在容器中执行 push_back 的迭代器。队列没有 push_back 方法,这就是为什么 back_insert_iterator 不起作用。在队列中添加元素的方法是push,因此push_insert_iterator的想法......
  • @sbi:考虑到将元素“插入”到队列中是通过 queue::push() 函数进行的,push_inserter 不是一个坏名字,恕我直言
  • @Éric:不,不是。一点也不。但是,我的评论是在 Luc 的回答有 push_inserter 之前写的。 :)
【解决方案4】:

std::queue 不是 STL 中的基本容器之一。它是一个使用基本 STL 容器之一构建的容器适配器(在本例中为顺序容器之一,std::vectorstd::dequestd::list)。它专为 FIFO 行为而设计,并且不提供在您希望 insert_iterator 工作的给定迭代器处的随机插入。因此不可能像这样使用队列。

我能想到的最简单的方法是:

class PushFunctor
{
public:
    PushFunctor(std::queue<int>& q) : myQ(q)
    {
    }
    void operator()(int n)
    {
        myQ.push(n);
    }

private:
    std::queue<int>& myQ;
};

并像这样使用它:

queue<int> q;
PushFunctor p(q);
std::for_each(v.begin(), v.end(), p);

【讨论】:

    【解决方案5】:

    我很确定它不会工作——队列提供push,但插入迭代器希望使用push_frontpush_back。没有真正的理由你不能写自己的push_insert_iterator(或任何你喜欢的名字),但这有点痛苦......

    【讨论】:

      【解决方案6】:

      在这个简单的例子中,你可以这样写:

      vector<int> v;
      v.push_back( 1 );
      v.push_back( 2 );
      
      queue<int, vector<int> > q(v);
      

      这将复制vector 并将其用作queue 的底层容器。

      当然,如果您需要在队列构建后将事物入队,这种方法将不起作用。

      【讨论】:

      • 非常好。不幸的是,我的示例过于简单,这不是我想要做的。
      • 队列不能使用向量。容器必须支持pop_front
      • 啊!你说得对。由于模板,我的测试没有显示这一点。好吧,我猜你仍然可以创建一个队列,让你检查front()back()...:P
      【解决方案7】:

      insert_iteratorback_insert_iterator 仅适用于具有(分别)insertpush_back 方法的容器(或适配器) - queue 没有这些方法。你可以编写自己的迭代器以这些为模型,如下所示:

      template <typename Container> 
      class push_iterator : public iterator<output_iterator_tag,void,void,void,void>
      {
      public:
          explicit push_iterator(Container &c) : container(c) {}
      
          push_iterator &operator*() {return *this;}
          push_iterator &operator++() {return *this;}
          push_iterator &operator++(int) {return *this;}
      
          push_iterator &operator=(typename Container::const_reference value)
          {
               container.push(value);
               return *this;
          }
      private:
          Container &container;
      };
      

      除非这样的东西已经存在,但我很确定它不存在。

      【讨论】:

      • 我正要接受这个答案,它看起来很棒(虽然我还没有尝试过)但查尔斯在帖子中击败了你。
      • 如果标准库中有这样一个迭代器就好了。但是,关于您的实现,我认为让解引用运算符返回代理对象而不是当前对象会更安全。实际上,代码本身允许您执行push_iterator&lt; queue&lt; int &gt; &gt; it; it = 42;,这是错误的(它还允许您执行******it,这不太正确)。 operator* 应该返回一个定义 operator= 的对象。
      • 您还可以在答案中添加通常的帮助函数,允许省略容器类型:template &lt; typename Container &gt; push_iterator&lt; Container &gt; pusher( Container &amp; c ) { return push_iterator&lt; Container &gt;( c ); }
      【解决方案8】:

      不幸的是,std::queue 将名为 push_back 的函数“改编”为 push,这意味着标准的 back_insert_iterator 不起作用。

      可能最简单的方法(尽管在概念上很难看)是用一个寿命短的容器适配器适配器来调整容器适配器[原文如此](哎呀!),它的寿命与后插入迭代器一样长。

      template<class T>
      class QueueAdapter
      {
      public:
          QueueAdapter(std::queue<T>& q) : _q(q) {}
          void push_back(const T& t) { _q.push(t); }
      
      private:
          std::queue<T>& _q;
      };
      

      这样使用:

      std::queue<int> qi;
      
      QueueAdapter< std::queue<int> > qiqa( qi );
      
      std::copy( v.begin(), v.end(), std::back_inserter( qiqa ) );
      

      【讨论】:

      • 天才。整洁、漂亮,并暴露了使用名称 push 而不是 push_back 的奇怪决定。
      • 这不是push_back,因为插入发生的点在概念上并不重要。此外,push_backpriority_queue 意味着什么?
      • 从概念上讲,这是有道理的,我只是“不幸地”使用了这项工作,因为这意味着它使用 back_insert_iterator 关闭,这可能非常有用。
      • 我同意 Kylotan 对该问题的评论。如果您想做超出队列 ADT 允许的事情(基本上是推送和弹出),那么与队列适配器抗争几乎没有意义。 - 不过,为您的优雅回答 +1。
      【解决方案9】:

      c++11

      std::for_each( v.begin(), v.end(), [&q1](int data) {  q1.push(data); }  );
      

      和 c++14

      std::for_each( v.begin(), v.end(), [&q1](auto data) {  q1.push(data); }  );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-01
        • 2022-01-14
        • 1970-01-01
        • 2015-02-19
        • 2021-09-21
        • 2015-01-05
        相关资源
        最近更新 更多