std :: ітерація черги


78

Мені потрібно повторити std::queue. www.cplusplus.com каже:

За замовчуванням, якщо для певного класу черги не вказаний клас контейнера, використовується стандартний шаблон шаблону класу контейнера.

То чи можу я якось дістатися до основного деке та переглядати його?

Відповіді:


74

Якщо вам потрібно виконати ітерацію, queueтоді вам потрібно щось більше, ніж черга. Суть стандартних адаптерів контейнерів полягає у забезпеченні мінімального інтерфейсу. Якщо вам також потрібно виконати ітерацію, чому б просто не використовувати замість цього деке (або список)?


127
Хоча я знаю, що ви говорите, мені завжди не подобалось таке словосполучення "щось більше, ніж черга". Черга з переліченням все одно є чергою ... Крім того, спостерігайте, як dequeсаме випадково підтримується перерахування, абсолютно довільно. Ви можете стверджувати, що це dequeповинно бути настільки ж пуристичним, як queueі не підтримувати ітерацію, і якщо ви хочете її повторити, то хочете щось «більше»; наприклад a deque_enumerable. Однак це слизький схил, і моє особисте відчуття полягає в тому, що queueв першу чергу слід було підтримати перелік.
Роман Старков

7
@romkyns: Чи було б краще, якби я переформулював це: "Вам потрібно щось із розширеним інтерфейсом, ніж queueінтерфейс, тому вам слід вибрати об'єкт з відповідним інтерфейсом". Подобається вам це чи ні, ітерація не є частиною queueінтерфейсу, тому, якщо ви хочете ітерацію, вам потрібно вибрати щось інше.
CB Bailey

11
Оскільки мій варіант використання вимагає черги, але мені потрібно скинути її для налагодження та ведення журналу. Як правило, не конструктивно вважати, що плакати не знають, що роблять.
EML

5
@RomanStarkov - Здається, це повинно було бути можливим для queueпідтримки прямих ітераторів, але не зворотних ітераторів, не обтяжуючи жодної розумної реалізації, про яку я можу подумати. Думаю, професори CS101 могли б скаржитися на це ...
ТЕД

4
@EML - Моя потреба точно. Так чи інакше вимогами щодо налагодження часто нехтують, оскільки це потрібне лише
несамовитим

37

Незважаючи на те, що я погоджуюсь з іншими, що безпосереднє використання ітеративного контейнера є кращим рішенням, я хочу зазначити, що стандарт С ++ гарантує достатню підтримку рішення «зроби сам», якщо ти хочеш це з якихось причин.

А саме, ви можете успадкувати 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
@Deqing: праворуч; але перегляд базового контейнера не буде в порядку пріоритету.
Олексій Куканов

1
Чому перевизначити новий клас, а не використовувати dequeбезпосередньо ?!
Alexis Wilke

@Deqing див. Також це питання
Peter K

@AlexeyKukanov це не черга пріоритетів, а звичайна черга FIFO, тому це правильний порядок ітерацій
Ерік Брендель

@ErikBrendel, це було у відповідь на видалений коментар із запитанням, чи можна використовувати ту саму техніку з priority_queue.
Олексій Куканов

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()методу
Killzone Kid

1
@KillzoneKid Це правильно, адже std::queueправильний метод.front()
Пол Стіліан

4

Одним непрямим рішенням може бути використання std :: deque замість цього. Він підтримує всі операції черги, і ви можете перебирати їх, просто використовуючи for(auto& x:qu). Це набагато ефективніше, ніж використання тимчасової копії черги для ітерації.


2

Чому б просто не зробити копію черги, яку ви хочете повторити, і видалити елементи по черзі, друкуючи їх по ходу? Якщо ви хочете зробити більше з елементами під час ітерації, то черга - це неправильна структура даних.


6
Е, ні. Копіювання, а потім знищення черги - це значно більші витрати, ніж вам потрібно. Ось чому були винайдені ітератори.
Mac

1
Простіше: створення порожньої черги. Попустіть кожен елемент з основної черги, поки він не стане порожнім, обробіть його за бажанням і натисніть на порожню чергу. По завершенні встановіть основну чергу рівною порожній. Працює і для priority_queue. Застереження: Не захищено від потоків, якщо інший потік намагається отримати доступ до черги одночасно. Крім того, якщо ваш оригінал був розподілений за купою (створений через malloc/ new), обов’язково free/ deleteце інакше ви втратите пам’ять.
Даррел Хоффман,

-1: Я фактично отримую занадто низьку частоту кадрів для ДУЖЕ маленьких черг, які я копіюю (я не отримую 60 кадрів в секунду через такі копії кожного кадру, ДУЖЕ мало об’єктів - те, що мій графічний процесор повинен мати 300+ кадрів в секунду з вимкненою VSync). Мені потрібен спосіб повторити це без копіювання
Пол Стіліан

0

в той час як відповідь Олексія Куканова може бути більш ефективною, ви також можете пройти по черзі цілком природним чином, вискакуючи кожен елемент спереду черги, а потім відсуваючи його назад:

#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

Коротше: Ні.

Існує хакер, використовуйте вектор як підкладений контейнер, тому 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 стає копією q1.


Це рішення є дуже проблематичним, оскільки воно змінює чергу під час ітерації. Тільки уявіть, що станеться, якщо ви використаєте його в багатопотоковій програмі або на випадок, якщо зупините ітерацію посередині.
jackhab

@jackhab дякую. Ти правий. Це може бути проблемою. Але ми могли б використовувати семафор або мьютекс, щоб подолати цю проблему (як я це роблю в своєму присвоєнні операційній системі IPC і pthread).
shamiul97

-2

Якщо вам потрібно повторити чергу ... черга - це не той контейнер, який вам потрібен.
Чому ти вибрав чергу?
Чому б вам не взяти контейнер, який можна переглядати?


1. якщо ви виберете чергу, то скажете, що хочете обернути контейнер в інтерфейс "черги": - спереду - назад - push - поп - ...

якщо ви також хочете виконати ітерацію, у черзі неправильний інтерфейс. Черга - це адаптер, який забезпечує обмежену підмножину вихідного контейнера

2. Визначення черги - це FIFO, і за визначенням FIFO не є повторюваним


36
Я не ОП, але ось мої відповіді, якщо комусь цікаво: 1) Я вибрав чергу, бо хочу чергу. Я хочу поставити в чергу в одному кінці, а зняти в іншому. Хіба це не розумний вибір? 2) Не очевидно, що "черги" не можна перерахувати, а також яку структуру використовувати замість неї. Ваша відповідь була б більш корисною, якби ви пояснили, який контейнер використовувати замість цього.
Роман Старков

-2

std::queueє контейнерним адаптером, і ви можете вказати використовуваний контейнер (за замовчуванням використовується a deque). Якщо вам потрібна функціональність, що перевищує функцію адаптера, просто використовуйте безпосередньо той dequeчи інший контейнер.


4
Хоча ваша відповідь є правильною, в ній абсолютно не було потреби, оскільки у цього 2-річного запитання вже є дві відповіді, що говорять абсолютно однаково (причому одна з них є прийнятою відповіддю).
Крістіан Рау,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.