209

I am using std::queue for implementing JobQueue class. ( Basically this class process each job in FIFO manner). In one scenario, I want to clear the queue in one shot( delete all jobs from the queue). I don't see any clear method available in std::queue class.

How do I efficiently implement the clear method for JobQueue class ?

I have one simple solution of popping in a loop but I am looking for better ways.

//Clears the job queue
void JobQueue ::clearJobs()
 {
  // I want to avoid pop in a loop
    while (!m_Queue.empty())
    {
        m_Queue.pop();
    }
}
Sisir
  • 4,584
  • 4
  • 26
  • 37
aJ.
  • 34,624
  • 22
  • 86
  • 128
  • 4
    Note [`deque`](http://www.cplusplus.com/reference/deque/deque/clear/) supports [clear](http://stackoverflow.com/questions/3874624/why-stdqueue-doesnt-support-clear-function?lq=1) – bobobobo May 24 '13 at 00:37

12 Answers12

320

A common idiom for clearing standard containers is swapping with an empty version of the container:

void clear( std::queue<int> &q )
{
   std::queue<int> empty;
   std::swap( q, empty );
}

It is also the only way of actually clearing the memory held inside some containers (std::vector)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 52
    Better yet is `std::queue().swap(q)`. With copy and swap idiom, all this should be equivalent to `q = std::queue()`. – Alexandre C. Nov 19 '10 at 16:42
  • 12
    While `std::queue().swap(q)` is equivalent to the code above, `q = std::queue()` need not be equivalent. Since there is no transfer of ownership in the assignment of the allocated memory some containers (like vector) might just call the destructors of the previously held elements and set the *size* (or equivalent operation with the stored pointers) without actually releasing the memory. – David Rodríguez - dribeas Nov 20 '10 at 19:44
  • 1
    In `std::queue().swap(q)`, isn't the temporary supposed to be `const`? – André Caron Aug 31 '11 at 19:40
  • @André Caron: No, temporaries are the result of an rvalue expression and as such it can only be used in limited ways (for example, it cannot be used as the lhs of an assignment, or bound to a reference), but that does not mean that it is const those limitations make the expression: `q.swap(std::queue())` invalid (the temporary cannot be bound by the reference argument of `swap`). A temporary can also be const: with `const type f();`, the expressión `f()` is an rvalue expression that yields a const temporary, but const-ness and temporaries are orthogonal concepts in the language. – David Rodríguez - dribeas Aug 31 '11 at 21:41
  • 7
    `queue` doesn't have a `swap(other)` method, so `queue().swap(q)` doesn't compile. I think you have to use the generic `swap(a, b)`. – Dustin Boswell Oct 05 '11 at 07:59
  • 1
    A short look into `queue` implementation of MSVC shows it's based on `deque`, which has a destructor that does `while (!empty()) pop_back();`. So, nothing gained apart from more complicated user code. – Thorbjørn Lindeijer Mar 20 '12 at 14:50
  • 3
    @ThorbjørnLindeijer: In C++03 you are right, in C++11 queues do have *swap* as a member function, and additionally there is a free function overload that will swap two queues of the same type. – David Rodríguez - dribeas Mar 20 '12 at 14:58
  • @DavidRodríguez-dribeas The availability of a swap member function doesn't mean destroying the queue got any more efficient in C++11, right? You swap a queue with an empty one efficiently, but the next thing is destroying the original queue, which is still linear in its number of elements. – Thorbjørn Lindeijer Mar 27 '12 at 20:29
  • 13
    @ThorbjørnLindeijer: From the perspective of the users of the original queue, those elements do not exist. You are correct in that they will be destroyed one after the other and the cost is linear, but they are not accessible by anyone else other than the local function. In a multithreaded environment, you would lock, swap a non-temporary queue with the original one, unlock (to allow for concurrent accesses) and let the swapped queue die. This way you can move the cost of destruction outside of the critical section. – David Rodríguez - dribeas Mar 27 '12 at 21:34
  • Dustin Boswell: it compiles on MSVC 2012 and does not compile in gcc 4.7. – Naszta Apr 29 '13 at 20:18
  • Is there a reason for using `std::swap(q, empty)` instead of just `swap(q, empty)` and relying on ADL? – Nemo Jan 17 '14 at 01:05
  • @Nemo: There is no reason not to rely on ADL, but in this case the type is well known to be in `::std` – David Rodríguez - dribeas Jan 17 '14 at 14:00
  • @DavidRodríguez `printf` is also well-known to be in `::std`, but I usually omit it :-). Just making sure I was not missing something. Thanks. – Nemo Jan 17 '14 at 16:41
  • @Nemo: Interesting example, if `printf` was *just* in `::std` and you ommitted it, and since ADL cannot add `::std`, the omission would cause a compiler error. – David Rodríguez - dribeas Jan 17 '14 at 18:08
  • @DavidRodríguez: If wishes were horses... My point is that since it is redundant, the decision comes down to which is easier to read. I did choose my example carefully. As you know, the C++ spec only requires `` to provide `::std::printf`, not `printf`. Nevertheless, I still use just `printf` in practice to avoid clutter. My personal aesthetic sense is to omit the namespace on `swap` as well, so I was curious if you had some technical rationale. (I believe I know the language pretty well, but I also believe you know it better.) – Nemo Jan 17 '14 at 18:51
  • I would not suggest using swap because apart from lack of readability and additional boilerplate code it is actually slower than assignment to an empty object (in C++03). – tim Feb 25 '15 at 16:24
  • @tim: that does not guarantee that the memory will be released, which is the point of the question (whether that is a sensible concern or not is a different question, but assigning from an empty queue may destroy all objects but not release the underlying buffer; assignment from an empty objet is equilvalent to `clear()`) – David Rodríguez - dribeas Feb 25 '15 at 17:00
  • Late comment for late readers: No matter which way we ever might try to clear the the queue or deque – it cannot ever be anything else than linear (general case) as for each element contained the *destructor* needs to be called anyway! Skipping would only be possible for POD types (special case). This applies, too, for `std::vector` or any other container. – Aconcagua May 18 '22 at 12:08
  • So this is O(n) or O(1)? – Rajas Paunikar Feb 04 '23 at 09:49
56

Yes - a bit of a misfeature of the queue class, IMHO. This is what I do:

#include <queue>
using namespace std;;

int main() {
    queue <int> q1;
    // stuff
    q1 = queue<int>();  
}
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
35

Author of the topic asked how to clear the queue "efficiently", so I assume he wants better complexity than linear O(queue size). Methods served by David Rodriguez, anon have the same complexity: according to STL reference, operator = has complexity O(queue size). IMHO it's because each element of queue is reserved separately and it isn't allocated in one big memory block, like in vector. So to clear all memory, we have to delete every element separately. So the straightest way to clear std::queue is one line:

while(!Q.empty()) Q.pop();
Melebius
  • 6,183
  • 4
  • 39
  • 52
janis
  • 351
  • 3
  • 2
  • 5
    You can't just look at the O complexity of the operation if you are operating on real data. I would take a `O(n^2)` algorithm over a `O(n)` algorithm if the constants on the linear operation make it slower than the quadratic for all `n < 2^64`, unless I had some strong reason to believe I had to search the IPv6 address space or some other specific problem. Performance in reality is more important to me than performance at the limit. – David Stone Apr 16 '12 at 06:26
  • 4
    This better answer than accepted answer because internally queue does this anyway when getting destroyed. So the accepted answer is O(n) plus it does extra allocations and initializations for brand new queue. – Shital Shah Dec 02 '16 at 01:08
  • 1
    Remember, O(n) means less than or equal to n complexity. So, yes, in some cases like queue>, it will need to destroy each element 1 by 1, which will be slow either way, but in queue, memory actually is allocated in one big block, and so it does not need to destroy the internal elements, and so queue's destructor can use a single efficient free() operation which almost certainly takes less than O(n) time. – Benjamin Jun 08 '19 at 13:23
33

In C++11 you can clear the queue by doing this:

std::queue<int> queue;
// ...
queue = {};
Danny_ds
  • 11,201
  • 1
  • 24
  • 46
kolage
  • 577
  • 9
  • 15
  • 6
    hey am just wondering if its O(n) or O(1) ? – Tilak Madichetti Jan 17 '21 at 09:28
  • 1
    @TilakMadichetti In the general case O(n) because the destructor has to be called for each contained element. This is also the case for `clear()` methods such as in `std::vector`. For POD elements, no element-wise destructor call is necessary, it should then be O(1) for `vector` but for `queue` it depends on the underlying container. The default `std::deque` reserves `M` chunks of memory where `M` is proportional to `n` (number of elements). So for default `std::queue` this it still O(n). – phinz Feb 23 '23 at 06:34
20

Apparently, there are two most obvious ways to clear std::queue: swapping with empty object and assignment to empty object.

I would suggest using assignment because it simply faster, more readable, and unambiguous.

I measured performance using following simple code and I found that swapping in C++03 version works 70-80% slower than assignment to an empty object. In C++11 there is no difference in performance, however. Anyway, I would go with assignment.

#include <algorithm>
#include <ctime>
#include <iostream>
#include <queue>
#include <vector>

int main()
{
    std::cout << "Started" << std::endl;

    std::queue<int> q;

    for (int i = 0; i < 10000; ++i)
    {
        q.push(i);
    }

    std::vector<std::queue<int> > queues(10000, q);

    const std::clock_t begin = std::clock();

    for (std::vector<int>::size_type i = 0; i < queues.size(); ++i)
    {
        // OK in all versions
        queues[i] = std::queue<int>();

        // OK since C++11
        // std::queue<int>().swap(queues[i]);

        // OK before C++11 but slow
        // std::queue<int> empty;
        // std::swap(empty, queues[i]);
    }

    const double elapsed = double(clock() - begin) / CLOCKS_PER_SEC;

    std::cout << elapsed << std::endl;

    return 0;
}
tim
  • 788
  • 4
  • 14
  • 2
    Results with C++11: `queues[i] = std::queue();`: 1.168, `std::queue().swap(queues[i]);`: 1.151, `std::queue empty; std::swap(empty, queues[i]);`: 1.164, `while (!queues[i].empty()) queues[i].pop();`: 0.189. Last one is the fastest by far. Thanks for the test code! – Burak Aug 15 '20 at 13:24
8

You could create a class that inherits from queue and clear the underlying container directly. This is very efficient.

template<class T>
class queue_clearable : public std::queue<T>
{
public:
    void clear()
    {
        c.clear();
    }
};

Maybe your a implementation also allows your Queue object (here JobQueue) to inherit std::queue<Job> instead of having the queue as a member variable. This way you would have direct access to c.clear() in your member functions.

typ1232
  • 5,535
  • 6
  • 35
  • 51
  • 14
    STL containers are not designed to be inherited from. In this case you're probably okay because you're not adding any additional member variables, but it's not a good thing to do in general. – bstamour Apr 29 '13 at 20:40
3

Assuming your m_Queue contains integers:

std::queue<int>().swap(m_Queue)

Otherwise, if it contains e.g. pointers to Job objects, then:

std::queue<Job*>().swap(m_Queue)

This way you swap an empty queue with your m_Queue, thus m_Queue becomes empty.

3

I do this (Using C++14):

std::queue<int> myqueue;
myqueue = decltype(myqueue){};

This way is useful if you have a non-trivial queue type that you don't want to build an alias/typedef for. I always make sure to leave a comment around this usage, though, to explain to unsuspecting / maintenance programmers that this isn't crazy, and done in lieu of an actual clear() method.

void.pointer
  • 24,859
  • 31
  • 132
  • 243
2

I'd rather not rely on swap() or setting the queue to a newly created queue object, because the queue elements are not properly destroyed. Calling pop()invokes the destructor for the respective element object. This might not be an issue in <int> queues but might very well have side effects on queues containing objects.

Therefore a loop with while(!queue.empty()) queue.pop();seems unfortunately to be the most efficient solution at least for queues containing objects if you want to prevent possible side effects.

Marste
  • 627
  • 7
  • 22
  • 4
    `swap()` or assignment calls the destructor on the now-defunct queue, which calls the destructors of all objects on the queue. Now, if your queue has objects that are actually pointers, that's a different issue -- but a simple `pop()` won't help you there, either. – jhfrontz Jan 20 '16 at 22:54
  • 1
    Why unfortunately? It's elegant and simple. –  Jan 17 '20 at 15:50
1

Using a unique_ptr might be OK.
You then reset it to obtain an empty queue and release the memory of the first queue. As to the complexity? I'm not sure - but guess it's O(1).

Possible code:

typedef queue<int> quint;

unique_ptr<quint> p(new quint);

// ...

p.reset(new quint);  // the old queue has been destroyed and you start afresh with an empty queue
display name
  • 4,165
  • 2
  • 27
  • 52
Ronnie
  • 11
  • 1
  • 1
    If you choose to empty the queue by deleting it, that's OK, but that's not what the question is about, and I don't see why unique_ptr comes in. – manuell Jan 10 '15 at 17:48
1

Another option is to use a simple hack to get the underlying container std::queue::c and call clear on it. This member must be present in std::queue as per the standard, but is unfortunately protected. The hack here was taken from this answer.

#include <queue>

template<class ADAPTER>
typename ADAPTER::container_type& get_container(ADAPTER& a)
{
    struct hack : ADAPTER
    {
        static typename ADAPTER::container_type& get(ADAPTER& a)
        {
            return a .* &hack::c;
        }
    };
    return hack::get(a);
}

template<typename T, typename C>
void clear(std::queue<T,C>& q)
{
    get_container(q).clear();
}

#include <iostream>
int main()
{
    std::queue<int> q;
    q.push(3);
    q.push(5);
    std::cout << q.size() << '\n';
    clear(q);
    std::cout << q.size() << '\n';
}
Ruslan
  • 18,162
  • 8
  • 67
  • 136
0

swap is not more efficient than "=". .

template <class T> void swap (T& a, T& b)
{
  T c(std::move(a)); a=std::move(b); b=std::move(c);
}

swap just use std::move. but for empty queue, std::move is useless.

my_queue = empty_queue; or my_queue = std::move(empty_queue);

徐国庆
  • 1
  • 1