âšī¸ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 0.1 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://www.studyplan.dev/pro-cpp/queue |
| Last Crawled | 2026-04-15 12:38:21 (2 days ago) |
| First Indexed | 2023-08-27 10:59:59 (2 years ago) |
| HTTP Status Code | 200 |
| Meta Title | Introduction to Queues and std::queue in C++ | A Practical Guide |
| Meta Description | UPDATED FOR C++23 | Learn the fundamentals of queues and their applications in C++ with the std::queue class template | Clear explanations and simple code examples |
| Meta Canonical | null |
| Boilerpipe Text | A queue is a data structure that replicates the properties of a real-life queue. They are ubiquitous in programming, with many use cases that we'll cover soon.
The defining characteristic of a queue is that objects leave in the same order they were added. Because of this, a queue is sometimes described as a first-in, first-out (FIFO) container.
We can visualize a queue as a sequential container, where objects are only added at the back of the queue, and only removed from the front.
Adding an object to a queue is often referred to as
pushing
it, or
enqueuing
it. Removing an object from the front of a queue is referred to as
popping
it, or
dequeuing
it.
Use Cases
Queues are everywhere in programming. Their use cases broadly fall into three categories -
simulation
,
asynchronous communication
, and
decoupling
.
Simulating a Real Queue
The most obvious use for a queue data structure is to simulate an actual, real-world queue. For example, if we're making an online service, we may find ourselves having a surge of popularity that overloads our infrastructure.
One way to prepare for this is to build a queue system, where users need to wait until we have enough capacity to let them in.
Asynchronous Communication between Systems
Sometimes, our systems communicating with each other through simple function calls can present issues. For example, imagine we're running a popular video hosting site. Users can upload their videos, which we compress, and then release to the public.
Compressing videos can take a long time. We don't want users waiting until that completes before we tell them their upload was successful. So, we can make these processes asynchronous, with the help of a queue.
When users upload their video, rather than compressing it within the same call stack, we can simply add the video to a queue, and consider the "uploading" part of the process complete. We can tell users their upload was successful.
Our compression system can run as a separate step, grabbing videos from the queue and compressing them without holding any other process back.
Decoupling Modules
The third use of a queue is more concealed from users, as it's a way to design our systems to make them more modular. When we're building a complex project, like a large game, we will have dozens of systems.
All our systems need an organized way to communicate with each other, and having an event queue is a common approach to take. We have a dedicated lesson on event queues, and how to implement them later in the course.
The
std::queue
Type
The C++ standard library has an implementation of a queue ready for us to use. It's available as part of the
<queue>
header. It is a template class that requires us to specify the type of object our queue will contain. In this example, we create a queue of integers:
#
include
<queue>
int
main
(
)
{
std
::
queue
<
int
>
Numbers
;
}
Copying and Moving Queues
We can also construct a queue with initial values by copying or moving another queue:
#
include
<queue>
int
main
(
)
{
std
::
queue
<
int
>
Numbers
;
std
::
queue
<
int
>
Copy
{
Numbers
}
;
std
::
queue
<
int
>
Move
{
std
::
move
(
Numbers
)
}
;
}
We cover copy semantics, move semantics, and
std::move
in more detail here:
Copy Semantics and Return Value Optimization
Learn how to control exactly how our objects get copied, and take advantage of copy elision and return value optimization (RVO)
Move Semantics
Learn how we can improve the performance of our types using move constructors, move assignment operators and
std::move()
When initializing a
std::queue
with values, we can, we can omit the template parameter. This will let the compiler infer it, using Class Template Argument Deduction (CTAD). Below, the compiler will deduce both
Copy
and
Move
should have a type of
std::queue<int>
:
#
include
<queue>
int
main
(
)
{
std
::
queue
<
int
>
Numbers
;
std
::
queue Copy
{
Numbers
}
;
std
::
queue Move
{
std
::
move
(
Numbers
)
}
;
}
Constructing Queues from Other Containers
We can also construct a queue directly from certain types of containers, such as a
std::list
:
#
include
<queue>
#
include
<list>
int
main
(
)
{
std
::
list Nums
{
1
,
2
,
3
}
;
std
::
queue NumsQueue
{
Nums
}
;
}
There is a little more nuance here on what type of containers we can use, and the implications on our queue. We cover this later in the lesson, in the section on container adapters.
Constructing Queues from Iterators
The queue class does not have a constructor that lets us provide a list of initial values. However, we can initialize a queue by passing an iterator pair denoting the start and end of a range of initial values.
We introduced iterators in a dedicated lesson earlier in the course:
Iterators
This lesson provides an in-depth look at iterators in C++, covering different types like forward, bidirectional, and random access iterators, and their practical uses.
Note, the iterator-based constructor for
std::queue
is a C++23 addition, so will not yet be supported by all compilers:
#
include
<queue>
#
include
<vector>
int
main
(
)
{
std
::
vector
<
int
>
Source
{
1
,
2
,
3
}
;
std
::
queue
<
int
>
Numbers
{
Source
.
begin
(
)
,
Source
.
end
(
)
}
;
}
Again, we can use class template argument deduction:
#
include
<queue>
#
include
<vector>
int
main
(
)
{
std
::
vector
<
int
>
Source
{
1
,
2
,
3
}
;
std
::
queue Numbers
{
Source
.
begin
(
)
,
Source
.
end
(
)
}
;
}
Adding (Pushing) Objects
Similar to most other sequential containers in the standard library, we have two ways to add objects to our queue.
emplace()
The
emplace()
method will construct our object directly into the queue. If the object we want to add doesn't already exist, this is the preferred method for performance reasons. Constructing an object in place tends to be faster than constructing it elsewhere, and then moving it into the container.
The
emplace()
method passes any arguments along to the constructor of the type stored in our queue:
#
include
<queue>
#
include
<string>
class
Character
{
public
:
Character
(
std
::
string Name
)
:
Name
{
Name
}
{
}
std
::
string Name
;
}
;
int
main
(
)
{
std
::
queue
<
Character
>
Q
;
Q
.
emplace
(
"Legolas"
)
;
}
push()
If our object already exists, we can use the queue's
push()
method to copy or move it into our container:
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
Character Legolas
{
"Legolas"
}
;
Character Gimli
{
"Gimli"
}
;
std
::
queue
<
Character
>
Q
;
// Copy
Q
.
push
(
Legolas
)
;
// Move
Q
.
push
(
std
::
move
(
Gimli
)
)
;
}
Accessing Queue Elements
Queues only provide easy access to two of their objects - the object at the front of the queue, and the object at the back.
front()
The queue's
front
method returns a reference to the object at the front of our queue. By definition of a queue, the object at the front is the object that has been in the queue the longest.
#
include
<iostream>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
queue
<
Character
>
Q
;
Q
.
emplace
(
"Legolas"
)
;
Q
.
emplace
(
"Gimli"
)
;
Q
.
emplace
(
"Gandalf"
)
;
std
::
cout
<<
Q
.
front
(
)
.
Name
<<
" is at the front of the queue"
;
}
Legolas is at the front of the queue
back()
We can retrieve a reference to the object at the end of our queue using the
back
method. By definition of a queue, the back of the queue is the object that was most recently added:
#
include
<iostream>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
queue
<
Character
>
Q
;
Q
.
emplace
(
"Legolas"
)
;
std
::
cout
<<
"The last character in the queue is "
<<
Q
.
back
(
)
.
Name
;
}
The last character in the queue is Legolas
We can't access other arbitrary elements of the queue. By design, queues are more restrictive than more general containers like
std::vector
.
Removing (Popping) Objects
To preserve the first-in-first-out rule of queues, we can only remove objects from the front of the queue. We do this using the
pop()
method:
#
include
<iostream>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
queue
<
Character
>
Q
;
Q
.
emplace
(
"Legolas"
)
;
Q
.
emplace
(
"Gimli"
)
;
Q
.
emplace
(
"Gandalf"
)
;
std
::
cout
<<
Q
.
front
(
)
.
Name
<<
" is at the front of the queue\n"
;
Q
.
pop
(
)
;
std
::
cout
<<
Q
.
front
(
)
.
Name
<<
" is now at the front"
;
}
Legolas is at the front of the queue
Gimli is now at the front
Checking the Queue Size
The
queue
class has two methods to determine how many elements it contains:
size()
The size method returns a
size_type
integer, telling us how many objects are in the queue:
#
include
<iostream>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
queue
<
Character
>
Q
;
Q
.
emplace
(
"Legolas"
)
;
Q
.
emplace
(
"Gimli"
)
;
Q
.
emplace
(
"Gandalf"
)
;
std
::
cout
<<
Q
.
size
(
)
<<
" objects are in the queue\n"
;
Q
.
pop
(
)
;
std
::
cout
<<
Q
.
size
(
)
<<
" objects are now in the queue"
;
}
3
objects are in the queue
2
objects are now in the queue
empty()
Where we want to compare the
size()
of the queue to
0
, we can use the more explicit
empty()
method:
#
include
<iostream>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
queue
<
Character
>
Q
;
Q
.
emplace
(
"Legolas"
)
;
if
(
!
Q
.
empty
(
)
)
{
std
::
cout
<<
"The queue is not empty\n"
;
}
Q
.
pop
(
)
;
if
(
Q
.
empty
(
)
)
{
std
::
cout
<<
"Now it is"
;
}
}
The queue is
not
empty
Now it is
Practical Queue Example
The typical approach for consuming a queue uses a
while
loop that continues whilst
Queue.empty()
is false. Within the body of the loop, we access the
front()
object, do what we need to do with it, and then
pop()
it off.
It looks something like this:
#
include
<iostream>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
queue
<
Character
>
Q
;
Q
.
emplace
(
"Legolas"
)
;
Q
.
emplace
(
"Gimli"
)
;
Q
.
emplace
(
"Gandalf"
)
;
while
(
!
Q
.
empty
(
)
)
{
std
::
cout
<<
Q
.
front
(
)
.
Name
<<
" has entered the game\n"
;
Q
.
pop
(
)
;
}
std
::
cout
<<
"The queue is empty!"
;
}
Legolas has entered the game
Gimli has entered the game
Gandalf has entered the game
The queue is empty
!
Container Adaptors
The C++ std::queue is an example of a
container adaptor
. It does not implement the storage mechanism for our objects - rather, it defers that to some other container, and provides us with an abstraction that acts like a queue.
By default, the container underlying
std::queue
is a
std::deque
. We cover deques (double-ended queues) in a later lesson.
We can change the underlying container by passing it to the second argument when constructing our queue.
The default
std::deque
is appropriate here, but we can use a different container class, including our own if preferred. In the following example, we use a doubly-linked list,
std::list
instead:
#
include
<iostream>
#
include
<list>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
queue
<
Character
,
std
::
list
<
Character
>>
Q
;
Q
.
emplace
(
"Legolas"
)
;
std
::
cout
<<
Q
.
front
(
)
.
Name
<<
" is at the front of the queue"
;
}
Legolas is at the front of the queue
For the
std::queue
adaptor to be compatible with a container, the container needs to have the following methods:
front()
- returns a reference to the object at the start of the container
back()
- returns a reference to the object at the end of the container
push_back()
- adds an object to the end of the container. Called by the
push()
method of
std::queue
.
emplace_back()
- constructs an object in the end of the container. Called by the
emplace()
method of
std::queue
pop_front()
- removes the object at the start of the container. Called by the
pop()
method of
std::queue
Whilst not enforced by the compiler, our container should naturally be efficient at running those methods if we want our queue to be performant.
Additionally, the underlying container should not consume an excessive amount of resources that are not required when the container is only being used in the context of a queue.
Initializing a Queue from a Container
We can initialize a queue using a collection of the queue's underlying type. For example, if our queue is using a
std::list
as the underlying container, we can initialize our queue by copying that
std::list
:
#
include
<iostream>
#
include
<list>
#
include
<queue>
#
include
<string>
class
Character
{
/*...*/
}
int
main
(
)
{
std
::
list
<
Character
>
Characters
{
"Legolas"
,
"Gandalf"
,
"Gimli"
}
;
std
::
queue
<
Character
,
std
::
list
<
Character
>>
Q
{
Characters
}
;
std
::
cout
<<
Q
.
front
(
)
.
Name
<<
" is at the front of the queue"
;
}
Class Template Argument Deduction
When initializing a queue from a container, we can use Class Template Argument Deduction (CTAD) to omit the template parameters. This is because the compiler can examine the type of argument we're passing to the constructor.
In this case, we're passing a
std::list<Character>
, so the compiler can infer our
std::queue
will be containing
Character
objects, so we'll construct a
std::queue<Character, std::list>
:
int
main
(
)
{
std
::
list
<
Character
>
Characters
{
}
;
std
::
queue Q
{
Characters
}
;
}
Moving the Container
If we don't need to copy our original container to create the queue, we can instead move it using
std::move
:
#
include
<iostream>
#
include
<list>
#
include
<queue>
int
main
(
)
{
std
::
list Nums
{
1
,
2
,
3
}
;
std
::
queue Q
{
std
::
move
(
Nums
)
}
;
std
::
cout
<<
Q
.
front
(
)
<<
" is at the front of the queue"
;
}
Summary
In this lesson, we covered the queue data structure, and specifically the standard library's implementation:
std::queue
. The key takeaways are:
Queues are FIFO data structures with various applications in programming.
The
std::queue
class template provides a convenient way to create and use queues in C++.
Objects are added to the back of the queue using
emplace()
or
push()
and removed from the front using
pop()
.
The
front()
and
back()
methods allow accessing the elements at the front and back of the queue.
std::queue
is a container adaptor that can use different underlying containers, such as
std::deque
or
std::list
. |
| Markdown | [STUDYPLAN.dev](https://www.studyplan.dev/)
[C++](https://www.studyplan.dev/cpp)
[CMake](https://www.studyplan.dev/cmake)
[SDL](https://www.studyplan.dev/sdl3)
Dark ModeToggle theme
[Log In](https://www.studyplan.dev/login)
[Introduction to Queues and `std::queue`](https://www.studyplan.dev/pro-cpp/queue#introduction-to-queues-and-stdqueue)[Use Cases](https://www.studyplan.dev/pro-cpp/queue#use-cases)[The `std::queue` Type](https://www.studyplan.dev/pro-cpp/queue#the-stdqueue-type)[Adding (Pushing) Objects](https://www.studyplan.dev/pro-cpp/queue#adding-pushing-objects)[Accessing Queue Elements](https://www.studyplan.dev/pro-cpp/queue#accessing-queue-elements)[Removing (Popping) Objects](https://www.studyplan.dev/pro-cpp/queue#removing-popping-objects)[Checking the Queue Size](https://www.studyplan.dev/pro-cpp/queue#checking-the-queue-size)[Practical Queue Example](https://www.studyplan.dev/pro-cpp/queue#practical-queue-example)[Container Adaptors](https://www.studyplan.dev/pro-cpp/queue#container-adaptors)[Initializing a Queue from a Container](https://www.studyplan.dev/pro-cpp/queue#initializing-a-queue-from-a-container)[Summary](https://www.studyplan.dev/pro-cpp/queue#summary)
# Introduction to Queues and `std::queue`
Learn the fundamentals and applications of queues with the `std::queue` container.
Ryan McCombe
Updated
2 years ago
1225
A queue is a data structure that replicates the properties of a real-life queue. They are ubiquitous in programming, with many use cases that we'll cover soon.
The defining characteristic of a queue is that objects leave in the same order they were added. Because of this, a queue is sometimes described as a first-in, first-out (FIFO) container.
We can visualize a queue as a sequential container, where objects are only added at the back of the queue, and only removed from the front.
Adding an object to a queue is often referred to as ***pushing*** it, or ***enqueuing*** it. Removing an object from the front of a queue is referred to as ***popping*** it, or ***dequeuing*** it.

## Use Cases
Queues are everywhere in programming. Their use cases broadly fall into three categories - ***simulation***, ***asynchronous communication***, and ***decoupling***.
### Simulating a Real Queue
The most obvious use for a queue data structure is to simulate an actual, real-world queue. For example, if we're making an online service, we may find ourselves having a surge of popularity that overloads our infrastructure.
One way to prepare for this is to build a queue system, where users need to wait until we have enough capacity to let them in.
### Asynchronous Communication between Systems
Sometimes, our systems communicating with each other through simple function calls can present issues. For example, imagine we're running a popular video hosting site. Users can upload their videos, which we compress, and then release to the public.
Compressing videos can take a long time. We don't want users waiting until that completes before we tell them their upload was successful. So, we can make these processes asynchronous, with the help of a queue.
When users upload their video, rather than compressing it within the same call stack, we can simply add the video to a queue, and consider the "uploading" part of the process complete. We can tell users their upload was successful.
Our compression system can run as a separate step, grabbing videos from the queue and compressing them without holding any other process back.
### Decoupling Modules
The third use of a queue is more concealed from users, as it's a way to design our systems to make them more modular. When we're building a complex project, like a large game, we will have dozens of systems.
All our systems need an organized way to communicate with each other, and having an event queue is a common approach to take. We have a dedicated lesson on event queues, and how to implement them later in the course.
## The `std::queue` Type
The C++ standard library has an implementation of a queue ready for us to use. It's available as part of the `<queue>` header. It is a template class that requires us to specify the type of object our queue will contain. In this example, we create a queue of integers:
```
1#include <queue>
2
3int main() {
4 std::queue<int> Numbers;
5}
```
### Copying and Moving Queues
We can also construct a queue with initial values by copying or moving another queue:
```
1#include <queue>
2
3int main() {
4 std::queue<int> Numbers;
5 std::queue<int> Copy{Numbers};
6 std::queue<int> Move{std::move(Numbers)};
7}
```
We cover copy semantics, move semantics, and `std::move` in more detail here:
### Copy Semantics and Return Value Optimization
Learn how to control exactly how our objects get copied, and take advantage of copy elision and return value optimization (RVO)
[View Related Lesson](https://www.studyplan.dev/pro-cpp/copy-semantics)
### Move Semantics
Learn how we can improve the performance of our types using move constructors, move assignment operators and `std::move()`
[View Related Lesson](https://www.studyplan.dev/pro-cpp/move-semantics)
When initializing a `std::queue` with values, we can, we can omit the template parameter. This will let the compiler infer it, using Class Template Argument Deduction (CTAD). Below, the compiler will deduce both `Copy` and `Move` should have a type of `std::queue<int>`:
```
1#include <queue>
2
3int main() {
4 std::queue<int> Numbers;
5 std::queue Copy{Numbers};
6 std::queue Move{std::move(Numbers)};
7}
```
### Constructing Queues from Other Containers
We can also construct a queue directly from certain types of containers, such as a `std::list`:
```
1#include <queue>
2#include <list>
3
4int main() {
5 std::list Nums{1, 2, 3};
6 std::queue NumsQueue{Nums};
7}
```
There is a little more nuance here on what type of containers we can use, and the implications on our queue. We cover this later in the lesson, in the section on container adapters.
### Constructing Queues from Iterators
The queue class does not have a constructor that lets us provide a list of initial values. However, we can initialize a queue by passing an iterator pair denoting the start and end of a range of initial values.
We introduced iterators in a dedicated lesson earlier in the course:
### Iterators
This lesson provides an in-depth look at iterators in C++, covering different types like forward, bidirectional, and random access iterators, and their practical uses.
[View Related Lesson](https://www.studyplan.dev/pro-cpp/iterators)
Note, the iterator-based constructor for `std::queue` is a C++23 addition, so will not yet be supported by all compilers:
```
1#include <queue>
2#include <vector>
3
4int main() {
5 std::vector<int> Source{1, 2, 3};
6 std::queue<int> Numbers{
7 Source.begin(), Source.end()};
8}
```
Again, we can use class template argument deduction:
```
1#include <queue>
2#include <vector>
3
4int main() {
5 std::vector<int> Source{1, 2, 3};
6 std::queue Numbers{
7 Source.begin(), Source.end()};
8}
```
## Adding (Pushing) Objects
Similar to most other sequential containers in the standard library, we have two ways to add objects to our queue.
### `emplace()`
The `emplace()` method will construct our object directly into the queue. If the object we want to add doesn't already exist, this is the preferred method for performance reasons. Constructing an object in place tends to be faster than constructing it elsewhere, and then moving it into the container.
The `emplace()` method passes any arguments along to the constructor of the type stored in our queue:
```
1#include <queue>
2#include <string>
3
4class Character {
5 public:
6 Character(std::string Name) : Name{Name} {}
7 std::string Name;
8};
9
10int main() {
11 std::queue<Character> Q;
12 Q.emplace("Legolas");
13}
```
### `push()`
If our object already exists, we can use the queue's `push()` method to copy or move it into our container:
```
1#include <queue>
2#include <string>
3
Character Class|Show4class Character {/*...*/}
10
11int main() {
12 Character Legolas{"Legolas"};
13 Character Gimli{"Gimli"};
14
15 std::queue<Character> Q;
16
17 // Copy
18 Q.push(Legolas);
19
20 // Move
21 Q.push(std::move(Gimli));
22}
```
## Accessing Queue Elements
Queues only provide easy access to two of their objects - the object at the front of the queue, and the object at the back.
### `front()`
The queue's `front` method returns a reference to the object at the front of our queue. By definition of a queue, the object at the front is the object that has been in the queue the longest.
```
1#include <iostream>
2#include <queue>
3#include <string>
4
Character Class|Show5class Character {/*...*/}
11
12int main() {
13 std::queue<Character> Q;
14 Q.emplace("Legolas");
15 Q.emplace("Gimli");
16 Q.emplace("Gandalf");
17
18 std::cout << Q.front().Name
19 << " is at the front of the queue";
20}
```
```
1Legolas is at the front of the queue
```
### `back()`
We can retrieve a reference to the object at the end of our queue using the `back` method. By definition of a queue, the back of the queue is the object that was most recently added:
```
1#include <iostream>
2#include <queue>
3#include <string>
4
Character Class|Show5class Character {/*...*/}
11
12int main() {
13 std::queue<Character> Q;
14 Q.emplace("Legolas");
15
16 std::cout
17 << "The last character in the queue is "
18 << Q.back().Name;
19}
```
```
1The last character in the queue is Legolas
```
We can't access other arbitrary elements of the queue. By design, queues are more restrictive than more general containers like `std::vector`.
## Removing (Popping) Objects
To preserve the first-in-first-out rule of queues, we can only remove objects from the front of the queue. We do this using the `pop()` method:
```
1#include <iostream>
2#include <queue>
3#include <string>
4
Character Class|Show5class Character {/*...*/}
11
12int main() {
13 std::queue<Character> Q;
14 Q.emplace("Legolas");
15 Q.emplace("Gimli");
16 Q.emplace("Gandalf");
17
18 std::cout
19 << Q.front().Name
20 << " is at the front of the queue\n";
21
22 Q.pop();
23
24 std::cout << Q.front().Name
25 << " is now at the front";
26}
```
```
1Legolas is at the front of the queue
2Gimli is now at the front
```
## Checking the Queue Size
The `queue` class has two methods to determine how many elements it contains:
### `size()`
The size method returns a `size_type` integer, telling us how many objects are in the queue:
```
1#include <iostream>
2#include <queue>
3#include <string>
4
Character Class|Show5class Character {/*...*/}
11
12int main() {
13 std::queue<Character> Q;
14 Q.emplace("Legolas");
15 Q.emplace("Gimli");
16 Q.emplace("Gandalf");
17
18 std::cout << Q.size()
19 << " objects are in the queue\n";
20
21 Q.pop();
22
23 std::cout << Q.size()
24 << " objects are now in the queue";
25}
```
```
13 objects are in the queue
22 objects are now in the queue
```
### `empty()`
Where we want to compare the `size()` of the queue to `0`, we can use the more explicit `empty()` method:
```
1#include <iostream>
2#include <queue>
3#include <string>
4
Character Class|Show5class Character {/*...*/}
11
12int main() {
13 std::queue<Character> Q;
14 Q.emplace("Legolas");
15
16 if (!Q.empty()) {
17 std::cout << "The queue is not empty\n";
18 }
19
20 Q.pop();
21
22 if (Q.empty()) {
23 std::cout << "Now it is";
24 }
25}
```
```
1The queue is not empty
2Now it is
```
## Practical Queue Example
The typical approach for consuming a queue uses a `while` loop that continues whilst `Queue.empty()` is false. Within the body of the loop, we access the `front()` object, do what we need to do with it, and then `pop()` it off.
It looks something like this:
```
1#include <iostream>
2#include <queue>
3#include <string>
4
Character Class|Show5class Character {/*...*/}
11
12int main() {
13 std::queue<Character> Q;
14 Q.emplace("Legolas");
15 Q.emplace("Gimli");
16 Q.emplace("Gandalf");
17
18 while (!Q.empty()) {
19 std::cout << Q.front().Name
20 << " has entered the game\n";
21 Q.pop();
22 }
23
24 std::cout << "The queue is empty!";
25}
```
```
1Legolas has entered the game
2Gimli has entered the game
3Gandalf has entered the game
4The queue is empty!
```
## Container Adaptors
The C++ std::queue is an example of a ***container adaptor***. It does not implement the storage mechanism for our objects - rather, it defers that to some other container, and provides us with an abstraction that acts like a queue.
By default, the container underlying `std::queue` is a `std::deque`. We cover deques (double-ended queues) in a later lesson.
We can change the underlying container by passing it to the second argument when constructing our queue.
The default `std::deque` is appropriate here, but we can use a different container class, including our own if preferred. In the following example, we use a doubly-linked list, `std::list` instead:
```
1#include <iostream>
2#include <list>
3#include <queue>
4#include <string>
5
Character Class|Show6class Character {/*...*/}
12
13int main() {
14 std::queue<Character, std::list<Character>> Q;
15 Q.emplace("Legolas");
16 std::cout << Q.front().Name
17 << " is at the front of the queue";
18}
```
```
1Legolas is at the front of the queue
```
For the `std::queue` adaptor to be compatible with a container, the container needs to have the following methods:
- `front()` - returns a reference to the object at the start of the container
- `back()` - returns a reference to the object at the end of the container
- `push_back()` - adds an object to the end of the container. Called by the `push()` method of `std::queue`.
- `emplace_back()` - constructs an object in the end of the container. Called by the `emplace()` method of `std::queue`
- `pop_front()` - removes the object at the start of the container. Called by the `pop()` method of `std::queue`
Whilst not enforced by the compiler, our container should naturally be efficient at running those methods if we want our queue to be performant.
Additionally, the underlying container should not consume an excessive amount of resources that are not required when the container is only being used in the context of a queue.
## Initializing a Queue from a Container
We can initialize a queue using a collection of the queue's underlying type. For example, if our queue is using a `std::list` as the underlying container, we can initialize our queue by copying that `std::list`:
```
1#include <iostream>
2#include <list>
3#include <queue>
4#include <string>
5
Character Class|Show6class Character {/*...*/}
12
13int main() {
14 std::list<Character> Characters{
15 "Legolas", "Gandalf", "Gimli"};
16
17 std::queue<Character, std::list<Character>> Q{
18 Characters};
19
20 std::cout << Q.front().Name
21 << " is at the front of the queue";
22}
```
### Class Template Argument Deduction
When initializing a queue from a container, we can use Class Template Argument Deduction (CTAD) to omit the template parameters. This is because the compiler can examine the type of argument we're passing to the constructor.
In this case, we're passing a `std::list<Character>`, so the compiler can infer our `std::queue` will be containing `Character` objects, so we'll construct a `std::queue<Character, std::list>`:
```
1int main() {
2 std::list<Character> Characters{};
3 std::queue Q{Characters};
4}
```
### Moving the Container
If we don't need to copy our original container to create the queue, we can instead move it using `std::move`:
```
1#include <iostream>
2#include <list>
3#include <queue>
4
5int main() {
6 std::list Nums{1, 2, 3};
7
8 std::queue Q{std::move(Nums)};
9
10 std::cout << Q.front()
11 << " is at the front of the queue";
12}
```
## Summary
In this lesson, we covered the queue data structure, and specifically the standard library's implementation: `std::queue`. The key takeaways are:
- Queues are FIFO data structures with various applications in programming.
- The `std::queue` class template provides a convenient way to create and use queues in C++.
- Objects are added to the back of the queue using `emplace()` or `push()` and removed from the front using `pop()`.
- The `front()` and `back()` methods allow accessing the elements at the front and back of the queue.
- `std::queue` is a container adaptor that can use different underlying containers, such as `std::deque` or `std::list`.
References
Lesson History
Next Lesson
Lesson 60 of 128
### Priority Queues using `std::priority_queue`
Learn how to access objects based on their importance, and how to customise how that priority is determined
[Continue to Next Lesson](https://www.studyplan.dev/pro-cpp/priority-queue)
### Professional C++
4\.6 (28,562 reviews)
Comprehensive course covering advanced concepts, and how to use them on large-scale projects.
Course progress0/128 lessons
[View Full Course](https://www.studyplan.dev/pro-cpp)
#### Chapter Navigation
Chapter 8/15
##### Standard Library Data Structures
Previous
Next
[Nullable Values, `std::optional` and Monadic Operations](https://www.studyplan.dev/pro-cpp/optional)
[Constrained Dynamic Types using Unions and `std::variant`](https://www.studyplan.dev/pro-cpp/variants)
[Unconstrained Dynamic Types using Void Pointers and `std::any`](https://www.studyplan.dev/pro-cpp/any)
[Tuples and `std::tuple`](https://www.studyplan.dev/pro-cpp/tuple)
[Introduction to Queues and `std::queue`](https://www.studyplan.dev/pro-cpp/queue)
[Priority Queues using `std::priority_queue`](https://www.studyplan.dev/pro-cpp/priority-queue)
[Introduction to Stacks using `std::stack`](https://www.studyplan.dev/pro-cpp/stacks)
[Double-Ended Queues using `std::deque`](https://www.studyplan.dev/pro-cpp/deque)
## Questions & Answers
Answers are generated by AI models and may not have been reviewed. Be mindful when running any code on your device.
Queue vs Stack: What's the Difference?
What is the main difference between a queue and a stack data structure in C++?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-vs-stack)
Checking Queue Size: `empty()` vs `size()`
When should I use the `empty()` method instead of comparing the `size()` to `0` to check if a queue is empty?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-empty-vs-size)
Queue Safety: Accessing `front()` and `back()` on Empty Queues
Is it safe to call `front()` or `back()` on an empty queue? What happens if I do?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-front-back-safety)
Moving Queues: Efficiency and Use Cases
When should I use move semantics with `std::queue`, and how does it improve performance?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-move-semantics)
Exception Safety in Queue Operations
How can I ensure exception safety when performing operations on `std::queue`?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-exception-safety)
Choosing the Underlying Container for `std::queue`
How do I decide which underlying container to use for `std::queue` in my program?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-underlying-container)
Thread Safety with `std::queue`
Is `std::queue` thread-safe? How can I use queues safely in a multi-threaded environment?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-thread-safety)
Using `std::queue` as a Message Buffer
How can I use `std::queue` as a message buffer for communication between threads or processes?
[Read answer](https://www.studyplan.dev/pro-cpp/queue/q/queue-as-message-buffer)
Or Ask your Own Question
Get an immediate answer to your specific question using our AI assistant
[Log In to Ask a Question](https://www.studyplan.dev/login?return=%2Fpro-cpp%2Fqueue%2Fq)
[STUDYPLAN.dev](https://www.studyplan.dev/)
[Contact](https://www.studyplan.dev/contact)[Privacy Policy](https://www.studyplan.dev/privacy-policy)[Terms of Use](https://www.studyplan.dev/terms-of-use)
Lesson Contents
Course Details |
| Readable Markdown | A queue is a data structure that replicates the properties of a real-life queue. They are ubiquitous in programming, with many use cases that we'll cover soon.
The defining characteristic of a queue is that objects leave in the same order they were added. Because of this, a queue is sometimes described as a first-in, first-out (FIFO) container.
We can visualize a queue as a sequential container, where objects are only added at the back of the queue, and only removed from the front.
Adding an object to a queue is often referred to as ***pushing*** it, or ***enqueuing*** it. Removing an object from the front of a queue is referred to as ***popping*** it, or ***dequeuing*** it.

Use Cases
Queues are everywhere in programming. Their use cases broadly fall into three categories - ***simulation***, ***asynchronous communication***, and ***decoupling***.
### Simulating a Real Queue
The most obvious use for a queue data structure is to simulate an actual, real-world queue. For example, if we're making an online service, we may find ourselves having a surge of popularity that overloads our infrastructure.
One way to prepare for this is to build a queue system, where users need to wait until we have enough capacity to let them in.
### Asynchronous Communication between Systems
Sometimes, our systems communicating with each other through simple function calls can present issues. For example, imagine we're running a popular video hosting site. Users can upload their videos, which we compress, and then release to the public.
Compressing videos can take a long time. We don't want users waiting until that completes before we tell them their upload was successful. So, we can make these processes asynchronous, with the help of a queue.
When users upload their video, rather than compressing it within the same call stack, we can simply add the video to a queue, and consider the "uploading" part of the process complete. We can tell users their upload was successful.
Our compression system can run as a separate step, grabbing videos from the queue and compressing them without holding any other process back.
### Decoupling Modules
The third use of a queue is more concealed from users, as it's a way to design our systems to make them more modular. When we're building a complex project, like a large game, we will have dozens of systems.
All our systems need an organized way to communicate with each other, and having an event queue is a common approach to take. We have a dedicated lesson on event queues, and how to implement them later in the course.
The `std::queue` Type
The C++ standard library has an implementation of a queue ready for us to use. It's available as part of the `<queue>` header. It is a template class that requires us to specify the type of object our queue will contain. In this example, we create a queue of integers:
```
#include <queue>
int main() {
std::queue<int> Numbers;
}
```
### Copying and Moving Queues
We can also construct a queue with initial values by copying or moving another queue:
```
#include <queue>
int main() {
std::queue<int> Numbers;
std::queue<int> Copy{Numbers};
std::queue<int> Move{std::move(Numbers)};
}
```
We cover copy semantics, move semantics, and `std::move` in more detail here:
### Copy Semantics and Return Value Optimization
Learn how to control exactly how our objects get copied, and take advantage of copy elision and return value optimization (RVO)
### Move Semantics
Learn how we can improve the performance of our types using move constructors, move assignment operators and `std::move()`
When initializing a `std::queue` with values, we can, we can omit the template parameter. This will let the compiler infer it, using Class Template Argument Deduction (CTAD). Below, the compiler will deduce both `Copy` and `Move` should have a type of `std::queue<int>`:
```
#include <queue>
int main() {
std::queue<int> Numbers;
std::queue Copy{Numbers};
std::queue Move{std::move(Numbers)};
}
```
### Constructing Queues from Other Containers
We can also construct a queue directly from certain types of containers, such as a `std::list`:
```
#include <queue>
#include <list>
int main() {
std::list Nums{1, 2, 3};
std::queue NumsQueue{Nums};
}
```
There is a little more nuance here on what type of containers we can use, and the implications on our queue. We cover this later in the lesson, in the section on container adapters.
### Constructing Queues from Iterators
The queue class does not have a constructor that lets us provide a list of initial values. However, we can initialize a queue by passing an iterator pair denoting the start and end of a range of initial values.
We introduced iterators in a dedicated lesson earlier in the course:
### Iterators
This lesson provides an in-depth look at iterators in C++, covering different types like forward, bidirectional, and random access iterators, and their practical uses.
Note, the iterator-based constructor for `std::queue` is a C++23 addition, so will not yet be supported by all compilers:
```
#include <queue>
#include <vector>
int main() {
std::vector<int> Source{1, 2, 3};
std::queue<int> Numbers{
Source.begin(), Source.end()};
}
```
Again, we can use class template argument deduction:
```
#include <queue>
#include <vector>
int main() {
std::vector<int> Source{1, 2, 3};
std::queue Numbers{
Source.begin(), Source.end()};
}
```
Adding (Pushing) Objects
Similar to most other sequential containers in the standard library, we have two ways to add objects to our queue.
### `emplace()`
The `emplace()` method will construct our object directly into the queue. If the object we want to add doesn't already exist, this is the preferred method for performance reasons. Constructing an object in place tends to be faster than constructing it elsewhere, and then moving it into the container.
The `emplace()` method passes any arguments along to the constructor of the type stored in our queue:
```
#include <queue>
#include <string>
class Character {
public:
Character(std::string Name) : Name{Name} {}
std::string Name;
};
int main() {
std::queue<Character> Q;
Q.emplace("Legolas");
}
```
### `push()`
If our object already exists, we can use the queue's `push()` method to copy or move it into our container:
```
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
Character Legolas{"Legolas"};
Character Gimli{"Gimli"};
std::queue<Character> Q;
// Copy
Q.push(Legolas);
// Move
Q.push(std::move(Gimli));
}
```
Accessing Queue Elements
Queues only provide easy access to two of their objects - the object at the front of the queue, and the object at the back.
### `front()`
The queue's `front` method returns a reference to the object at the front of our queue. By definition of a queue, the object at the front is the object that has been in the queue the longest.
```
#include <iostream>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::queue<Character> Q;
Q.emplace("Legolas");
Q.emplace("Gimli");
Q.emplace("Gandalf");
std::cout << Q.front().Name
<< " is at the front of the queue";
}
```
```
Legolas is at the front of the queue
```
### `back()`
We can retrieve a reference to the object at the end of our queue using the `back` method. By definition of a queue, the back of the queue is the object that was most recently added:
```
#include <iostream>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::queue<Character> Q;
Q.emplace("Legolas");
std::cout
<< "The last character in the queue is "
<< Q.back().Name;
}
```
```
The last character in the queue is Legolas
```
We can't access other arbitrary elements of the queue. By design, queues are more restrictive than more general containers like `std::vector`.
Removing (Popping) Objects
To preserve the first-in-first-out rule of queues, we can only remove objects from the front of the queue. We do this using the `pop()` method:
```
#include <iostream>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::queue<Character> Q;
Q.emplace("Legolas");
Q.emplace("Gimli");
Q.emplace("Gandalf");
std::cout
<< Q.front().Name
<< " is at the front of the queue\n";
Q.pop();
std::cout << Q.front().Name
<< " is now at the front";
}
```
```
Legolas is at the front of the queue
Gimli is now at the front
```
Checking the Queue Size
The `queue` class has two methods to determine how many elements it contains:
### `size()`
The size method returns a `size_type` integer, telling us how many objects are in the queue:
```
#include <iostream>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::queue<Character> Q;
Q.emplace("Legolas");
Q.emplace("Gimli");
Q.emplace("Gandalf");
std::cout << Q.size()
<< " objects are in the queue\n";
Q.pop();
std::cout << Q.size()
<< " objects are now in the queue";
}
```
```
3 objects are in the queue
2 objects are now in the queue
```
### `empty()`
Where we want to compare the `size()` of the queue to `0`, we can use the more explicit `empty()` method:
```
#include <iostream>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::queue<Character> Q;
Q.emplace("Legolas");
if (!Q.empty()) {
std::cout << "The queue is not empty\n";
}
Q.pop();
if (Q.empty()) {
std::cout << "Now it is";
}
}
```
```
The queue is not empty
Now it is
```
Practical Queue Example
The typical approach for consuming a queue uses a `while` loop that continues whilst `Queue.empty()` is false. Within the body of the loop, we access the `front()` object, do what we need to do with it, and then `pop()` it off.
It looks something like this:
```
#include <iostream>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::queue<Character> Q;
Q.emplace("Legolas");
Q.emplace("Gimli");
Q.emplace("Gandalf");
while (!Q.empty()) {
std::cout << Q.front().Name
<< " has entered the game\n";
Q.pop();
}
std::cout << "The queue is empty!";
}
```
```
Legolas has entered the game
Gimli has entered the game
Gandalf has entered the game
The queue is empty!
```
Container Adaptors
The C++ std::queue is an example of a ***container adaptor***. It does not implement the storage mechanism for our objects - rather, it defers that to some other container, and provides us with an abstraction that acts like a queue.
By default, the container underlying `std::queue` is a `std::deque`. We cover deques (double-ended queues) in a later lesson.
We can change the underlying container by passing it to the second argument when constructing our queue.
The default `std::deque` is appropriate here, but we can use a different container class, including our own if preferred. In the following example, we use a doubly-linked list, `std::list` instead:
```
#include <iostream>
#include <list>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::queue<Character, std::list<Character>> Q;
Q.emplace("Legolas");
std::cout << Q.front().Name
<< " is at the front of the queue";
}
```
```
Legolas is at the front of the queue
```
For the `std::queue` adaptor to be compatible with a container, the container needs to have the following methods:
- `front()` - returns a reference to the object at the start of the container
- `back()` - returns a reference to the object at the end of the container
- `push_back()` - adds an object to the end of the container. Called by the `push()` method of `std::queue`.
- `emplace_back()` - constructs an object in the end of the container. Called by the `emplace()` method of `std::queue`
- `pop_front()` - removes the object at the start of the container. Called by the `pop()` method of `std::queue`
Whilst not enforced by the compiler, our container should naturally be efficient at running those methods if we want our queue to be performant.
Additionally, the underlying container should not consume an excessive amount of resources that are not required when the container is only being used in the context of a queue.
Initializing a Queue from a Container
We can initialize a queue using a collection of the queue's underlying type. For example, if our queue is using a `std::list` as the underlying container, we can initialize our queue by copying that `std::list`:
```
#include <iostream>
#include <list>
#include <queue>
#include <string>
class Character {/*...*/}
int main() {
std::list<Character> Characters{
"Legolas", "Gandalf", "Gimli"};
std::queue<Character, std::list<Character>> Q{
Characters};
std::cout << Q.front().Name
<< " is at the front of the queue";
}
```
### Class Template Argument Deduction
When initializing a queue from a container, we can use Class Template Argument Deduction (CTAD) to omit the template parameters. This is because the compiler can examine the type of argument we're passing to the constructor.
In this case, we're passing a `std::list<Character>`, so the compiler can infer our `std::queue` will be containing `Character` objects, so we'll construct a `std::queue<Character, std::list>`:
```
int main() {
std::list<Character> Characters{};
std::queue Q{Characters};
}
```
### Moving the Container
If we don't need to copy our original container to create the queue, we can instead move it using `std::move`:
```
#include <iostream>
#include <list>
#include <queue>
int main() {
std::list Nums{1, 2, 3};
std::queue Q{std::move(Nums)};
std::cout << Q.front()
<< " is at the front of the queue";
}
```
Summary
In this lesson, we covered the queue data structure, and specifically the standard library's implementation: `std::queue`. The key takeaways are:
- Queues are FIFO data structures with various applications in programming.
- The `std::queue` class template provides a convenient way to create and use queues in C++.
- Objects are added to the back of the queue using `emplace()` or `push()` and removed from the front using `pop()`.
- The `front()` and `back()` methods allow accessing the elements at the front and back of the queue.
- `std::queue` is a container adaptor that can use different underlying containers, such as `std::deque` or `std::list`. |
| Shard | 145 (laksa) |
| Root Hash | 16591698488056343945 |
| Unparsed URL | dev,studyplan!www,/pro-cpp/queue s443 |