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.