Standard Library[lib]

C++ queue: how std::queue works, with examples

Section
Standard Library [lib]
Standard
C++11
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
3 min read
In this answer7 clauses
  1. 1How do you add to a queue and read from it?
  2. 2Why does pop() return nothing?
  3. 3What happens if the queue is empty?
  4. 4How do you loop through a queue?
  5. 5Queue vs stack: what is the difference?
  6. 6What are std::deque and std::priority_queue?
  7. 7Which one should you use?

Abstract

A C++ queue is std::queue, from the header <queue>: elements come back in the order they went in, first in, first out. push() adds at the back, front() reads the oldest element and pop() removes it and returns nothing, so read front() first. Calling front(), back() or pop() on an empty queue is undefined behavior, so check empty() before them. A queue has no iterators: loop until empty() is true, reading front() and popping.

Three hatched blocks wait in a row inside an open channel. An arrow brings a new block into its right end; another carries a teal block out of its left end.
Figure 1 — First in, first out: a new element joins at the back, and the oldest leaves from the front.

A C++ queue is a std::queue, from the header <queue>, which hands elements back in the order they went in: first in, first out. push() adds an element at the back, front() reads the oldest one and pop() removes it. pop() returns nothing, so read front() first, and check empty() before either.

A print queue with three jobs:

[Example 1:
#include <iostream>
#include <queue>
#include <string>

int main() {
    std::queue<std::string> jobs;
    jobs.push("report.pdf");
    jobs.push("photo.png");
    jobs.push("invoice.pdf");

    std::cout << jobs.front() << '\n';  // report.pdf, the oldest
    jobs.pop();                         // removes report.pdf
    std::cout << jobs.front() << '\n';  // photo.png
    std::cout << jobs.size() << '\n';   // 2
}

— end example]

It printed:

[Example 2:
report.pdf
photo.png
2

— end example]

This answer is written for C++11, the standard that added emplace() (Since When’s C++11 table lists the rest of what it added). The samples were built as C++11 with GCC 16.2 and Clang 23.1 (libc++), and as C++17 with those two, MSVC 19.44 and Clang 19.1 (MSVC STL); MSVC has no C++11 mode. All four printed the same unless a sentence says otherwise.

How do you add to a queue and read from it?

push() adds an element at the back, and emplace() does the same but builds the element in place from its arguments. front() and back() read the two ends; size() counts the elements and empty() says whether there are none. The type in the angle brackets is what the queue holds, and a new queue starts empty:

[Example 3:
std::queue<std::string> jobs;
std::cout << std::boolalpha;
std::cout << jobs.empty() << '\n';  // true

jobs.push("report.pdf");            // adds at the back
jobs.emplace("photo.png");          // builds the string there
jobs.push("invoice.pdf");

std::cout << jobs.front() << '\n';  // report.pdf, the oldest
std::cout << jobs.back() << '\n';   // invoice.pdf, the newest

jobs.front() = "report-v2.pdf";     // front() is a reference
std::cout << jobs.front() << '\n';  // report-v2.pdf

std::cout << jobs.size() << '\n';   // 3
std::cout << jobs.empty() << '\n';  // false

— end example]

All four printed what the comments show. The members of std::queue:

Member What it does Returns
push(x) adds x at the back nothing
emplace(args) builds an element at the back from args nothing (since C++17, a reference to the new element)
front() reads the oldest element a reference to it
back() reads the newest element a reference to it
pop() removes the oldest element nothing
size() counts the elements the number
empty() checks for no elements true or false

Why does pop() return nothing?

pop() returns nothing: it removes the front element and its return type is void. The original STL’s documentation gives the reason: returning the element would take a copy, since a reference to it would dangle, and front() reads it without one. Code that expects the removed element from it does not compile:

[Example 4:
std::string job = jobs.pop();   // error: pop() returns void

— end example]

Clang said:

[Example 5:
error: no viable conversion from 'void' to 'std::string'

— end example]

MSVC reports the same line as error C2440, cannot convert from 'void'. To keep the element, copy it out first and pop second: std::string job = jobs.front();, then jobs.pop();.

What happens if the queue is empty?

Calling front(), back() or pop() on an empty queue is undefined behavior: the standard places no requirement on what the program then does, and no exception is promised. A queue passes those calls to the container underneath, and that container’s front(), back() and pop_front() require that it is not empty ([sequence.reqmts]). Test empty() first:

[Example 6:
void print_next(std::queue<std::string>& jobs) {
    if (!jobs.empty()) {
        std::cout << "printing " << jobs.front() << '\n';
        jobs.pop();
    } else {
        std::cout << "nothing to print\n";
    }
}

— end example]

Called twice on a queue holding only report.pdf, it printed printing report.pdf, then nothing to print.

How do you loop through a queue?

Loop while the queue is not empty, reading front() and then popping, until nothing is left:

[Example 7:
while (!jobs.empty()) {
    std::string job = jobs.front();  // read the oldest first
    jobs.pop();                      // then remove it
    std::cout << "printing " << job << '\n';
}
std::cout << jobs.size() << '\n';    // 0

— end example]

With the three jobs in it, all four printed printing report.pdf, printing photo.png and printing invoice.pdf in that order, then 0.

A queue has no iterators, so a range-for over it does not compile. for (const auto& job : jobs) stopped with Clang’s invalid range expression of type 'std::queue<std::string>' and no viable 'begin' function available; MSVC’s is error C3312, no callable 'begin' function found.

To print a queue and keep it, loop over a copy. A parameter taken by value is a copy:

[Example 8:
void print_all(std::queue<std::string> jobs) {  // takes a copy
    while (!jobs.empty()) {
        std::cout << jobs.front() << '\n';
        jobs.pop();                             // pops the copy
    }
}

— end example]

All four printed the three jobs, and the original queue still had a size() of 3 afterwards.

Queue vs stack: what is the difference?

A queue hands back the oldest element first, and a stack the newest: last in, first out. Side by side:

Property std::queue std::stack
Order first in, first out last in, first out
Adds push(), at the back push(), on top
Reads front(), the oldest top(), the newest
Removes pop(), the oldest pop(), the newest
pop() returns nothing nothing
Header <queue> <stack>
Top: std::queue holds report.pdf, photo.png and invoice.pdf in a row; push() enters at the right, pop() leaves at the left, front() marks report.pdf. Below: std::stack holds them stacked, invoice.pdf on top, where push(), pop() and top() act.
Figure 2 — Pushed in the same order, a queue's pop() removes the oldest element and a stack's pop() the newest.

The same three files as an undo history:

[Example 9:
std::stack<std::string> history;    // an undo history
history.push("report.pdf");
history.push("photo.png");
history.push("invoice.pdf");

std::cout << history.top() << '\n'; // invoice.pdf, the newest

while (!history.empty()) {
    std::cout << "undo " << history.top() << '\n';
    history.pop();
}

— end example]

All four printed invoice.pdf, then undo invoice.pdf, undo photo.png and undo report.pdf: the reverse of the order they went in. A std::stack has no iterators either.

What are std::deque and std::priority_queue?

A std::deque (header <deque>) is a double-ended queue that you can also index and loop over, and a std::priority_queue hands back the largest element first instead of the oldest.

The deque is the container that a std::queue and a std::stack keep their elements in unless you name another; the two are container adaptors, which wrap a container and let you reach only some of it. Use a std::deque directly when you need both ends, an index or a loop. Adding or removing at either end takes constant time, and so does reading by index:

[Example 10:
std::deque<std::string> jobs;
jobs.push_back("photo.png");
jobs.push_back("invoice.pdf");
jobs.push_front("report.pdf");      // jumps the line

std::cout << jobs[1] << '\n';       // photo.png

for (const auto& job : jobs)        // a deque has iterators
    std::cout << job << '\n';

jobs.pop_front();                   // removes report.pdf
jobs.pop_back();                    // removes invoice.pdf
std::cout << jobs.size() << ' ' << jobs.front() << '\n';

— end example]

All four printed photo.png for jobs[1], then report.pdf, photo.png and invoice.pdf from the loop, and last 1 photo.png.

A priority queue, std::priority_queue, is also in <queue>. It hands back the smallest element first when std::greater is its third template argument, and top() reads the next one:

[Example 11:
std::priority_queue<int> priorities;
priorities.push(3);
priorities.push(5);
priorities.push(1);

while (!priorities.empty()) {
    std::cout << priorities.top() << '\n';  // 5, 3, 1
    priorities.pop();
}
// ...
std::priority_queue<int, std::vector<int>, std::greater<int>>
    lowest_first;

— end example]

All four printed 5, 3 and 1 from the first, and 1, 3 and 5 from lowest_first after the same three pushes. For a type of your own, the comparison (std::less by default, or one you pass as the third argument) must be a strict weak ordering, a consistent “less than”: the same rule as a comparator for std::sort and std::map.

Which one should you use?

You need Use Header
First in, first out std::queue <queue>
Last in, first out std::stack <stack>
Both ends, an index or a loop std::deque <deque>
The largest or the smallest next std::priority_queue <queue>
A list you index and grow at the end std::vector <vector>

A checklist for queue code:

  • Include <queue> and <string> for a queue of strings.
  • Read front() before pop(): pop() returns nothing.
  • Check empty() before front(), back() or pop().
  • Loop with while (!jobs.empty()); for a range-for, keep the jobs in a std::deque.
  • To clear a queue, assign it an empty one: jobs = {};.

None of these containers has a lock of its own: two threads that share one need a std::mutex, held across empty(), front() and pop() together, not around each call alone. The rest of the containers and the library are in Standard library.

Questions

Is std::queue thread safe?

No. A std::queue has no lock of its own, so two threads that share one, either of them pushing or popping, need a std::mutex. Hold it across each whole step, empty(), front() and pop() together: a lock around each call alone still lets the other thread take the last element between empty() and front().

How do you clear a queue in C++?

A std::queue has no clear() member. Assign it an empty queue instead: jobs = {}; or jobs = std::queue<std::string>(); empties it, and both did on all four compilers.

What is the time complexity of push and pop on a queue?

On a std::queue and a std::stack, push() and pop() take amortized constant time: constant on average over many calls, whatever the size. On a std::priority_queue, push() and pop() are logarithmic in the number of elements, and top() is constant.

What container does std::queue use?

A std::deque by default, and std::stack uses one too. A second template argument picks another, such as std::queue<int, std::list<int>>: the container must provide front(), back(), push_back() and pop_front(), which std::deque and std::list do. std::priority_queue uses a std::vector by default.

Can you access the middle of a queue?

No. A std::queue gives access to its two ends only, through front() and back(), and has no index and no iterators. If you need an element in the middle, use a std::deque, which has both ends, an index and iterators.

Which header do you include for a queue?

Include <queue> for std::queue, and for std::priority_queue, which is declared in the same header. std::stack is in <stack> and std::deque in <deque>.

Sources

  1. std::queuecppreference.com
  2. queue (the original STL documentation, note 3)SGI Standard Template Library documentation, hosted by Boost
  3. std::queue::popcppreference.com
  4. [sequence.reqmts] RequirementsC++ standard working draft
  5. std::stackcppreference.com
  6. std::dequecppreference.com
  7. std::priority_queuecppreference.com

Read next

  1. C++ random number: how to generate one in a rangeStandard Library · Miriam Castellanos · written for C++11
  2. C++ string (std::string) explained with examplesStandard Library · Miriam Castellanos · written for C++17
  3. C++ getline: how to read a whole line of inputStandard Library · Miriam Castellanos · written for C++11