std :: queue迭代


78

我需要遍历std::queue。www.cplusplus.com说:

默认情况下,如果未为特定队列类指定容器类,则使用标准容器类模板双端队列。

那么我可以以某种方式到达队列的底层双端队列并对其进行迭代吗?

Answers:


74

如果您需要遍历a,queue那么您需要的不仅仅是队列。标准容器适配器的重点是提供最小的接口。如果还需要进行迭代,为什么不只使用双端队列(或列表)呢?


127
虽然我知道您在说什么,但我一直不喜欢这种说法“比排队更重要”。带有枚举的队列仍然是一个队列...另外,观察一下如何deque完全支持枚举。您可能会说,它deque应该和它一样纯粹queue,不支持迭代,如果要迭代它,则需要“更多”的东西。例如deque_enumerable。虽然这是一个湿滑的斜坡,但我个人的感觉是queue应该首先支持枚举。
罗曼·斯塔科夫

7
@romkyns:如果我改写它会更好:“您需要界面比queue界面更丰富的东西,所以您应该选择具有合适界面的对象”。不管喜欢与否,迭代不是queue界面的一部分,因此,如果要迭代,则需要选择其他内容。
CB Bailey 2010年

10
因为我的用例需要一个队列,但是我需要将其转出以进行调试和记录。假定海报不知道自己在做什么,通常并没有建设性。
EML

5
@RomanStarkov-似乎应该有可能queue支持正向迭代器,但不反向反向迭代器,而不会增加我能想到的任何合理实现。我想CS101教授可能对此有所抱怨……
TED

4
@EML-完全是我的需要。某种程度上,调试要求通常被忽略,只是
流浪

37

虽然我同意其他人的观点,即直接使用可迭代容器是首选解决方案,但我想指出,C ++标准可以保证对“自己动手”解决方案提供足够的支持,以防万一您出于任何原因而想要这样做。

即,您可以std::queue从其受保护成员继承并使用其受保护成员Container c;来访问基础容器的begin()和end()(前提是存在此类方法)。这是一个在VS 2010中运行并通过ideone测试的示例

#include <queue>
#include <deque>
#include <iostream>

template<typename T, typename Container=std::deque<T> >
class iterable_queue : public std::queue<T,Container>
{
public:
    typedef typename Container::iterator iterator;
    typedef typename Container::const_iterator const_iterator;

    iterator begin() { return this->c.begin(); }
    iterator end() { return this->c.end(); }
    const_iterator begin() const { return this->c.begin(); }
    const_iterator end() const { return this->c.end(); }
};

int main() {
    iterable_queue<int> int_queue;
    for(int i=0; i<10; ++i)
        int_queue.push(i);
    for(auto it=int_queue.begin(); it!=int_queue.end();++it)
        std::cout << *it << "\n";
    return 0;
}

4
@德庆:对; 但在基础容器上进行迭代将不会具有优先级。
Alexey Kukanov 2014年

1
为什么要重新定义新类而不deque直接使用?
亚历克西斯威尔克

@德庆也看到这个问题
彼得·K

@AlexeyKukanov这不是优先级队列,只是普通的FIFO队列,所以这是正确的迭代顺序
Erik Brendel

@ErikBrendel,这是对现在已删除的评论的回应,该评论询问是否可以对priority_queue使用相同的技术。
Alexey Kukanov

14

您可以将原始队列保存到临时队列。然后,您只需在临时队列上进行常规弹出操作即可遍历原始队列,例如:

queue tmp_q = original_q; //copy the original queue to the temporary queue

while (!tmp_q.empty())
{
    q_element = tmp_q.front();
    std::cout << q_element <<"\n";
    tmp_q.pop();
} 

最后,tmp_q将为空,但原始队列未更改。


3
std::queue似乎没有有.top()
杀戮地带孩子

1
@KillzoneKid是的,std::queue正确的方法是.front()
Paul Stelian

4

一种间接解决方案是改为使用std :: deque。它支持队列的所有操作,您可以使用进行迭代for(auto& x:qu)。这比使用队列的临时副本进行迭代要高效得多。


2

为什么不只复制要迭代的队列,然后一次删除一个项目,然后随手打印它们呢?如果要在迭代时对元素做更多的事情,那么队列是错误的数据结构。


6
呃没有。复制然后销毁队列比您需要的开销更多。这就是发明迭代器的原因。
Mac

1
更简单:创建空队列。将每个项目从主队列中弹出,直到将其清空为止,然后根据需要对其进行处理,然后将其推入空队列中。完成后,将主队列设置为等于空队列。也适用于priority_queue。警告:如果某些其他线程试图同时访问队列,则不是线程安全的。另外,如果原始文件是通过堆分配的(通过malloc/创建new),请确保将其分配给free/ delete,否则会泄漏内存。
Darrel Hoffman 2015年

-1:对于我要复制的非常小的队列,我的帧率实际上太低了(由于每帧这样的复制,对象很少,我没有得到60FPS的东西-我的GPU应该能够达到300+ FPS停用VSync)。我需要一种方法来遍历它不复制
保罗Stelian

0

尽管Alexey Kukanov的答案可能更有效,但是您也可以通过非常自然的方式遍历队列,方法是从队列的前面弹出每个元素,然后将其推到后面:

#include <iostream>
#include <queue>

using namespace std;

int main() {
    //populate queue
    queue<int> q;
    for (int i = 0; i < 10; ++i) q.push(i);

    // iterate through queue
    for (size_t i = 0; i < q.size(); ++i) {
        int elem = std::move(q.front());
        q.pop();
        elem *= elem;
        q.push(std::move(elem));
    }

    //print queue
    while (!q.empty()) {
        cout << q.front() << ' ';
        q.pop();
    }
}

输出:

0 1 4 9 16 25 36 49 64 81 

-1

简而言之:不。

有一个hack,使用向量作为底层容器,因此queue::front将返回有效引用,将其转换为迭代的指针,直到<=queue::back


2
您也可以直接使用双端队列(deque)-包含所有必要的方法作为队列,但也支持迭代
-Dewfy

-1

我用这样的东西。不是很复杂,但应该可以。

    queue<int> tem; 

    while(!q1.empty()) // q1 is your initial queue. 
    {
        int u = q1.front(); 

        // do what you need to do with this value.  

        q1.pop(); 
        tem.push(u); 
    }


    while(!tem.empty())
    {
        int u = tem.front(); 
        tem.pop(); 
        q1.push(u); // putting it back in our original queue. 
    }

之所以起作用,是因为当您从q1弹出某项并将其推入tem时,它将成为tem的第一个元素。因此,最终tem成为q1的副本。


该解决方案非常有问题,因为它在迭代过程中会修改队列。试想一下,如果在多线程程序中使用它,或者在中间停止迭代的情况下会发生什么。
jackhab

@jackhab谢谢。你是对的。那可能是个问题。但是我们可以使用信号量或互斥量来克服该问题(就像我在IPC和pthread的操作系统分配中所做的那样)。
shamiul97

-2

如果您需要迭代一个队列...队列不是您需要的容器。
你为什么选择队列?
为什么不带一个可以迭代的容器?


1.如果选择一个队列,则说要将容器包装到“队列”界面中:-正面-背面-推动-弹出-...

如果您还想迭代,则队列具有错误的接口。队列是提供原始容器的受限子集的适配器

2队列的定义是一个FIFO,根据定义FIFO是不可迭代的


36
我不是OP,但是如果有人好奇,这是我的答案:1)我选择一个队列是因为我想要一个队列。我想一端入队,另一端出队。这不是一个合理的选择吗?2)“队列”不是不可枚举的,也不是使用哪种结构并不明显。如果您解释要使用哪个容器,那么您的答案将会更有帮助。
罗曼·斯塔科夫

-2

std::queue是容器适配器,可以指定使用的容器(默认使用deque)。如果您需要适配器中没有的功能,则直接使用一个deque或另一个容器即可。


4
虽然您的答案是正确的,但是绝对不需要,因为这个已有2年历史的问题已经有两个答案说的完全相同(其中一个是公认的答案)。
Christian Rau 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.