Containers in C++
In this article, we will discuss the containers in C++ with their types, examples, and outputs.
An element collection can be stored and arranged using a data structure called a container in C++. In the Standard Library, C++ offers many container classes, each with a different set of features and functions intended to fulfill a certain function.
Container types in C++:
- Sequence Containers
- Associative Containers
- Unordered Associative Containers
- Container Adapters
An overview of the major container categories and the container classes that correspond with them is provided below:
1. Sequence Containers
The ability to store elements that can be accessed in a sequential order is made possible by sequential containers in C++. Arrays or linked lists are the internal data structures used to implement sequential containers.
Sequential Container Types:
There are various types of sequential containers. These are as follows:
- Vector
- Array
- Deque
- List
- Forward List
a) Vector: The containers for arrays that can alter in size are called vectors. Similar to arrays, the elements of vectors are stored in contiguous memory regions. In contrast to arrays, a vector's size can vary dynamically, and the container will take care of any storage needs.
Example:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> vec;
for (int i = 1; i <= 30; i = i * 4)
vec.push_back(i);
cout << "Output with vec.begin() and vec.end(): ";
for (auto i = vec.begin(); i != vec.end(); i++)
cout << *i << " ";
cout << "\nOutput with vec.rbegin() and vec.rend(): ";
for (auto ir = vec.rbegin(); ir != vec.rend(); ir++)
cout << *ir << " ";
return 0;
}
Output:
Output with vec.begin() and vec.end(): 1 4 16
Output with vec.rbegin() and vec.rend(): 16 4 1
Explanation:
In this example, we create a vector vec. The push_back() function was used to add elements to this vector. After that, we utilized the begin() and end(), rbegin(), and rend() methods to print the vector in a left-to-right or right-to-left direction, respectively.
b) Array: Array is a fixed-size array whose size is known at compile time. Containers with a set size are called arrays. A certain amount of elements are contained in them. Instead of having to go through the entire array, we can use array elements directly by specifying their index.
Example:
#include <iostream>
#include <array>
using namespace std;
int main()
{
//Creating an array
array<int, 5> arr = {10, 18, 15, 12, 13,};
// constructing an array iterator
array<int, 5>::iterator it;
// using the iterator to iterate through each entry of the array
cout << "The array is: ";
for(it = arr.begin(); it < arr.end(); it++)
{
cout << *it << " ";
}
cout << endl;
cout << "The size of the array is: " << arr.size() << endl;
return 0;
}
Output:
The array is: 10 18 15 12 13
The size of the array is: 5
Explanation:
The program above generated an array called arr using the array standard library. Afterward, we made use of a variety of array library's preset functions. The pointers indicate the array's start and last entries that the methods begin() and end() respectively return. Size() provides the array's size as a result.
c) Deque: A double-ended queue that effectively accommodates inserting and removing items from both ends. The double-ended queue is abbreviated as DEQUE. The size of a deque is a container that can change dynamically on both ends. Deque enables us to access each of its parts using random access iterators.
Example:
#include <iostream>
#include <deque>
using namespace std;
int main()
{
deque<int> dq;
dq.push_back(8); // added element at the back
dq.push_front(6); // added element at the front
dq.push_back(3); // added element at the back
dq.push_front(2); // added element at the front
deque<int>::iterator it;
cout << "The deque dq is: ";
for (it = dq.begin(); it != dq.end(); it++)
{
cout << *it << " ";
}
cout << "\ndq.size(): " << dq.size();
// removing the first element of a deque
dq.pop_front();
cout << "\nDeque after using pop_front() is: ";
for (it = dq.begin(); it != dq.end(); it++)
{
cout << *it << " ";
}
return 0;
}
Output:
The deque dq is: 2 6 8 3
dq.size(): 4
Deque after using pop_front() is: 6 8 3
Explanation:
A deque dq was produced in the example above. Using the push_back() and push_front() functions, we correspondingly added components to the back and front of the deque. After that, we used the size() method to determine the size of the deque. Finally, we used the pop_front() function to remove the deque's first element.
d) List: A doubly-linked list that facilitates quick insertion and removal at any point. Lists are sequence containers that enable insert and erase operations in constant time, wherever in a sequence they are allowed, just like forward lists. A doubly-linked list is how lists are implemented. In light of this, iteration is possible in both directions (front and back).
Example:
#include <iostream>
#include <iterator>
#include <list>
using namespace std;
int main()
{
//Defining a list
list<int> lst;
//Defining an iterator
list<int>::iterator it;
for (int i = 0; i < 7; i++)
{
// including a back element
lst.push_back(i * 5);
}
// printing the list
cout << "List (lst) is -> ";
for (it = lst.begin(); it != lst.end(); ++it)
{
cout << *it << " ";
}
cout << endl;
// the list's first and last elements are printed
cout << "lst.front() -> " << lst.front() << endl;
cout << "lst.back() -> " << lst.back() << endl;
// reversing the list
lst.reverse();
cout << "lst.reverse() -> ";
for (it = lst.begin(); it != lst.end(); ++it)
{
cout << *it << " ";
}
return 0;
}
Output:
List (lst) is -> 0 5 10 15 20 25 30
lst.front() -> 0
lst.back() -> 30
lst.reverse() -> 30 25 20 15 10 5 0
Explanation:
The push_back() function was used to add elements to the list lst that was formed in the aforementioned example. After that, the front() and back() procedures were used to print the list's first and last entries. Finally, the list was reversed using the reverse() function.
e) Forward List: Forward lists are sequence containers that enable insert and erase operations in constant time, regardless of where in the sequence they occur. Singly-linked lists implement the forward lists. As a result, each element of a forward list can be kept in a different location in memory.
Example:
#include <iostream>
#include <forward_list>
using namespace std;
int main()
{
//Creating a forward list
forward_list<int> fr_lst = {61, 12, 35, 24, 52};
// using push_front() to inserted new values
fr_lst.push_front(8);
fr_lst.push_front(10);
// printing the forward list
cout << "The forward list is: ";
for (int& i : fr_lst)
{
cout << i << " ";
}
cout << endl;
// Using pop_front(), delete the first value
fr_lst.pop_front();
// printing the revised forward list
cout << "Forward list after using pop_front: ";
for (int& i : fr_lst)
{
cout << i << " ";
}
return 0;
}
Output:
The forward list is: 10 8 61 12 35 24 52
Forward list after using pop_front: 8 61 12 35 24 52
Explanation:
In this example, the forward list fr_lst was made, and five additional members were added to it. After that, we utilized the push_front() function to add two more members to the list. Finally, the pop_front() function was used to eliminate the list's first element.
2. Associative Containers
Associative containers are ones in which an element's position is determined by its value. The position of an element is determined without taking the sequence of the elements' insertion into account. Keys, sometimes known as search keys, are used to access the contents of an associative container.
The four different types of associative containers
- Set
- Map
- Multiset
- Multimap
- Set
a) set is a collection of items, each of which (or each key) must be distinct. It is impossible to change an element once it has been added to a set. However, the element can be taken out of the set. They are stored in a sorted manner to ensure quick storage and retrieval of the items in a set.
Example:
#include <iostream>
#include <set>
using namespace std;
int main()
{
// declaring a set container empty
set<int> s1;
// declaring an iterator
set<int>::iterator it;
// The components should be inserted randomly
s1.insert(34);
s1.insert(53);
s1.insert(26);
s1.insert(50);
// A second attempt to add 50 to the set
s1.insert(50);
// printing set s1
cout << "The set s1 is -> ";
for (it = s1.begin(); it != s1.end(); it++)
{
cout << *it << " ";
}
cout << endl << endl;
// Delete the element in S1 with the value 50.
s1.erase(50);
cout << "Set s1 after using s1.erase(50) -> ";
for (it = s1.begin(); it != s1.end(); it++)
{
cout << *it << " ";
}
return 0;
}
Output:
The set s1 is -> 26 34 50 53
Set s1 after using s1.erase(50) -> 26 34 53
Explanation:
In this example, the number 50 was attempted to be inserted twice. In contrast, 50 was only added once to the set because every element in a set must be distinct. After that, an element was eliminated from the set by using the erase() function.
b) Map
Key-value pairs are the unit of storage for data in a map. A map container must have just one value assigned to each key, and all of the keys must be distinct. Both the key's associated value and its data type could be different. The components of a map are kept about the key. Any position in a map container can have items added to it or taken away.
Example:
#include <iostream>
#include <map>
using namespace std;
int main()
{
// declaring an empty map container
map<int, char> m1;
map<int, char>::iterator itr;
// insert elements
m1.insert(pair<int, char>(1, 'e'));
m1.insert(pair<int, char>(3, 'd'));
m1.insert(pair<int, char>(4, 'c'));
m1.insert(pair<int, char>(5, 'g'));
m1.insert(pair<int, char>(2, 'y'));
// printing map gquiz1
cout<< "The map m1 is: " <<endl;
cout<< "KEY -> VALUE" <<endl;
for (itr = m1.begin(); itr != m1.end(); ++itr)
{
cout<<" "<<itr->first << " -> "
<<itr->second <<endl;
}
cout<<endl;
// removeing the element with key = 4
m1.erase(4);
cout<< "Map after executing m1.erase(4) is: " <<endl;
cout<< "KEY -> VALUE" <<endl;
for (itr = m1.begin(); itr != m1.end(); ++itr)
{
cout<<" "<<itr->first << " -> "
<<itr->second <<endl;
}
return 0;
}
Output:
The map m1 is:
KEY -> VALUE
1 -> e
2 -> y
3 -> d
4 -> c
5 -> g
Map after executing m1.erase(4) is:
KEY -> VALUE
1 -> e
2 -> y
3 -> d
5 -> g
Explanation:
In the example above, we established a map and added elements with the help of the insert() function. After that, the element with key = 4 was eliminated using the erase() function.
c) Multiset
In contrast to sets, multisets are containers that permit the duplication of elements (keys). An element's value cannot be changed once it has been stored in a multiset, but it can be taken out of the container.
Example:
#include <iostream>
#include <set>
using namespace std;
int main()
{
// empty multiset container
multiset<int> ms1;
// insert elements in random order
ms1.insert(40);
ms1.insert(60);
ms1.insert(10);
ms1.insert(20);
// 50 will be added twice to the set
ms1.insert(50);
// printing multiset ms1
multiset<int>::iterator itr;
cout<< "The multiset ms1 is: " <<endl;
for (itr = ms1.begin(); itr != ms1.end(); itr++)
{
cout<< *itr<< " ";
}
cout<<endl<<endl;
//Remove all elements up to the element with the value 40 in ms1
cout<< "ms1 after removal of elements < 40: " <<endl;
ms1.erase(ms1.begin(), ms1.find(40));
for (itr = ms1.begin(); itr != ms1.end(); itr++)
{
cout<< *itr<< " ";
}
return 0;
}
Output:
The multiset ms1 is:
10 20 40 50 60
ms1 after removal of elements < 40:
40 50 60
d) Multimap
Map containers and multimap containers are similar. However, the latter allows the storage of duplicate keys. So, the key-value pairs have a one-to-many relationship with one another. The data type of a key in a multimap can vary from that of the value it corresponds to.
Example:
#include <iostream>
#include <map>
using namespace std;
int main()
{
// empty multimap container
multimap<int, int> m1;
multimap<int, int>::iterator itr;
// insert elements
m1.insert(pair<int, int>(43, 2));
m1.insert(pair<int, int>(30, 3));
m1.insert(pair<int, int>(38, 4));
m1.insert(pair<int, int>(24, 7));
m1.insert(pair<int, int>(45, 8));
// printing multimap m1
cout<< "The multimap m1 is: " <<endl;
cout<< "KEY -> VALUE" <<endl;
for (itr = m1.begin(); itr != m1.end(); itr++)
{
cout<< " " <<itr->first <<" -> "
<<itr->second <<endl;
}
cout<<endl;
// removing all elements up to key with value 30
m1.erase(m1.begin(), m1.find(30));
cout<< "m1 after removal of elements < key = 30: " <<endl;
cout<< "KEY -> VALUE" <<endl;
for (itr = m1.begin(); itr != m1.end(); itr++)
{
cout<< " " <<itr->first <<" -> "
<<itr->second <<endl;
}
return 0;
}
Output:
The multimap m1 is:
KEY -> VALUE
24 -> 7
30 -> 3
38 -> 4
43 -> 2
45 -> 8
m1 after removal of elements < key = 30:
KEY -> VALUE
30 -> 3
38 -> 4
43 -> 2
45 -> 8
Explanation:
In this example, we constructed a multimap. Here, we were able to insert the same key into the multimap twice because the multimap allows for duplicate key values. We also utilized the erase() function to eliminate all the entries with key values lower than 30.
3. Unordered Associative Containers
Unordered containers are those containers where the placement of the items is irrelevant, also known as unordered associative containers. Unordered containers do not store items by the values they contain or the order in which they were inserted. The positional order of n elements in an unordered container is ill-defined and may even fluctuate over time. These containers' elements can be reached via a hash(#).
In the C++ STL, there are four varieties of unordered containers:
- Unordered_set
- Unordered_multiset
- Unordered_map
- Unordered_multimap
a) Unordered_set
Unordered sets serve as storage spaces for distinct pieces that aren't organized in any specific way. An element's key alone serves as its value in an unordered set. Although they can be added and removed, an unordered set's elements cannot be changed.
Example:
#include <iostream>
#include <unordered_set>
using namespace std;
int main()
{
// declaring unordered set
unordered_set <char> set1 ;
// declaring an iterator
unordered_set<char> :: iterator itr;
// inserting elements to the set
set1.insert('s');
set1.insert('e');
set1.insert('r');
set1.insert('w');
set1.insert('t');
set1.insert('y');
// finding letter 's' in the set
char key_to_find = 's' ;
if (set1.find(key_to_find) == set1.end())
{
cout << "The key '" << key_to_find
<< "' is not present in set1" << endl << endl ;
}
else
{
cout << "The key '" << key_to_find
<< "' is present in set1" << endl << endl ;
}
// finding letter 'o' in the set
key_to_find = 'o';
if (set1.find(key_to_find) == set1.end())
{
cout << "The key '" << key_to_find
<< "' is not present in set1" << endl << endl ;
}
else
{
cout << "The key '" << key_to_find
<< "' is present in set1" << endl << endl ;
}
// printing all elements of the unordered set
cout << "All elements of the unordered set: " << endl;
for (itr = set1.begin(); itr != set1.end(); itr++)
{
cout << (*itr) << endl;
}
return 0;
}
Output:
The key 's' is present in set1
The key 'o' is not present in set1
All elements of the unordered set:
y
t
w
r
e
s
Explanation:
In the example above, we used the insert() function to add elements to the unordered set. The elements' and 'o' were checked to see if they were present in the unordered set using the find() function. We can see from this example that the order of the elements' insertion differs from the order in which they were printed. It indicates that the components of unordered sets are not kept in a set order.
b) Unordered_multiset
Unordered multisets and unordered sets are similar; the only distinction is that unordered multisets permit duplicate entries. The elements of an unordered multiset cannot be changed, much like in unordered sets. Only insertion and removal are possible.
Example:
#include <iostream>
#include <unordered_set>
#include <string>
int main()
{
// Create an unordered_multiset to store integers
std::unordered_multiset<int> numbers;
// Insert elements into the unordered_multiset
numbers.insert(10);
numbers.insert(20);
numbers.insert(30);
numbers.insert(20);
// Allow duplicates
// Iterate and print the elements in the unordered_multiset
std::cout << "Elements in the unordered_multiset: ";
for (const auto& num: numbers)
{
std::cout << num << " ";
}
std::cout << std::endl;
// Count the occurrences of a specific element
int target = 20;
int count = numbers.count(target);
std::cout << "Occurrences of " << target << ": " << count << std::endl;
// Erase a specific element
numbers.erase(target);
// Check if an element exists in the unordered_multiset
if (numbers.find(target) != numbers.end())
{
std::cout << target << " is found in the unordered_multiset." << std::endl;
}
else
{
std::cout << target << " is not found in the unordered_multiset." << std::endl;
}
// Clear all elements from the unordered_multiset
numbers.clear();
// Check if the unordered_multiset is empty
if (numbers.empty())
{
std::cout << "The unordered_multiset is empty." << std::endl;
}
else
{
std::cout << "The unordered_multiset is not empty." << std::endl;
}
return 0;
}
Output:
Elements in the unordered_multiset: 30 20 20 10
Occurrences of 20: 2
20 is not found in the unordered_multiset.
The unordered_multiset is empty.
Explanation:
In this example, the integers are stored in an unordered_multiset set called numbers. With the help of the insert function, we add numerous pieces to the multiset, including duplicates. After that, we go through it using a range-based for loop to print every element in the multiset.
Next, using the count function, we count how many times a certain element (like 20) appears. We may also take a specific element out of the multiset by using the erase() function. We use the find function to see if a specific element is present in the multiset.
After that, the multiset is empty once all of its items have been removed using the clear() function, and the empty() function is used to determine if it is.
c) Unordered_map
Unordered maps are those data structures that store items in key-value pairs with no regard to their order of occurrence. The key is used to uniquely identify each element in unordered maps. Each key in unordered maps must be distinct. The mapped value's data type may differ from the key's data type.
Example:
#include <iostream>
#include <unordered_map>
#include <string>
int main()
{
// Create an unordered_map to store student names and their corresponding ages
std::unordered_map<std::string, int> studentAges;
// Insert key-value pairs into the unordered_map
studentAges["Alice"] = 25;
studentAges["Bob"] = 22;
studentAges["Charlie"] = 27;
studentAges["Bob"] = 23; // Update Bob's age
// Iterate and print the key-value pairs in the unordered_map
std::cout << "Student names and their ages: " << std::endl;
for (const auto& pair : studentAges)
{
std::cout << pair.first << ": " << pair.second << std::endl;
}
// Find the age of a specific student
std::string targetName = "Charlie";
auto it = studentAges.find(targetName);
if (it != studentAges.end())
{
std::cout << targetName << "'s age is " << it->second << std::endl;
}
else
{
std::cout << "Student not found." << std::endl;
}
// Check if a student exists in the unordered_map
targetName = "Eve";
if (studentAges.count(targetName) > 0)
{
std::cout << targetName << " is found in the unordered_map." << std::endl;
}
else
{
std::cout << targetName << " is not found in the unordered_map." << std::endl;
}
// Erase a student from the unordered_map
targetName = "Bob";
studentAges.erase(targetName);
// Check if the unordered_map is empty
if (studentAges.empty())
{
std::cout << "The unordered_map is empty." << std::endl;
}
else
{
std::cout << "The unordered_map is not empty." << std::endl;
}
return 0;
}
Output:
Student names and their ages:
Charlie: 27
Bob: 23
Alice: 25
Charlie's age is 27
Eve is not found in the unordered_map.
The unordered_map is not empty.
Explanation:
In this example, we make an unordered_map called studentAges to hold the ages of the students, with their names serving as the keys (strings) and ages serving as the values (integers). Using the square bracket ([]) operator, we may add several key-value pairs to the unordered_map and update the value for a particular key.
After that, we employ a range-based for loop to run through the unordered_map and output each key-value pair. Keep in mind that because the unordered_map is an unordered container, the order of the components is not guaranteed.
The age of a particular student (for example, "Charlie") is then looked up using the search function. When a pupil is located, their age is printed. Additionally, we utilize the count function to determine whether a student with the name "Eve" or another similar name is present in the unordered_map.
Next, after removing a student from the unordered_map using the erase() function, we use the empty() function to determine whether the unordered_map is empty.
The unordered_map is helpful for applications where quick lookup times are crucial since it enables quick access to and retrieval of data based on their distinctive keys.
d) Unordered_multimap
The main distinction between unordered multimaps and unordered maps is that various items may have the same key in the latter. The mapped value(s) and the key's data type may differ.
Example:
#include <iostream>
#include <unordered_map>
#include <string>
int main()
{
// Create an unordered_multimap to store courses and their corresponding students
std::unordered_multimap<std::string, std::string> courseStudents;
// Insert key-value pairs into the unordered_multimap
courseStudents.insert({"Math", "Alice"});
courseStudents.insert({"Math", "Bob"});
courseStudents.insert({"Physics", "Charlie"});
courseStudents.insert({"Math", "Eve"});
courseStudents.insert({"Physics", "Bob"});
// Iterate and print the key-value pairs in the unordered_multimap
std::cout << "Courses and their students: " << std::endl;
for (const auto& pair: courseStudents)
{
std::cout << pair.first << ": " << pair.second << std::endl;
}
// Find students for a specific course
std::string targetCourse = "Math";
auto range = courseStudents.equal_range(targetCourse);
std::cout << "Students enrolled in " << targetCourse << ":" << std::endl;
for (auto it = range.first; it != range.second; ++it)
{
std::cout << it->second << std::endl;
}
// Check if a course exists in the unordered_multimap
targetCourse = "Chemistry";
if (courseStudents.count(targetCourse) > 0)
{
std::cout << targetCourse << " is found in the unordered_multimap." << std::endl;
}
else
{
std::cout << targetCourse << " is not found in the unordered_multimap." << std::endl;
}
// Erase a course from the unordered_multimap
targetCourse = "Physics";
courseStudents.erase(targetCourse);
// Check if the unordered_multimap is empty
if (courseStudents.empty())
{
std::cout << "The unordered_multimap is empty." << std::endl;
}
else
{
std::cout << "The unordered_multimap is not empty." << std::endl;
}
return 0;
}
Output:
Courses and their students:
Physics: Charlie
Physics: Bob
Math: Alice
Math: Eve
Math: Bob
Students enrolled in Math:
Alice
Eve
Bob
Chemistry is not found in the unordered_multimap.
The unordered_multimap is not empty.
Explanation:
In this example, the mapping of courses to the appropriate students is stored in an unordered_multimap called courseStudents. The values are the names of the students enrolled in those courses, and the keys are the titles of the courses (also strings).
Using the insert function, several key-value pairs are added to the unordered_multimap. We can associate several students with the same course because it is an unordered_multimap.
After that, we use a range-based for loop to crawl through the unordered_multimap and output each key-value pair. Since the unordered_multimap is an unordered container, there is no guarantee as to the order of the components.
Next, the equal_range function is used to locate all students registered in a certain course (for example, "Math"). We also employ the count function to determine whether a course with a specific name. For example, "Chemistry" exists in the unordered_multimap.
The next step is to remove a course (and the students associated with it) from the unordered_multimap using the erase function. Using the empty function, we verify whether the unordered_multimap is empty.
When you need to store several components with the same key but don't need the elements to be sorted according to their keys, the unordered_multimap can be helpful. It allows quick access to and retrieval of values based on their keys and is especially helpful for applications that need quick lookup speeds and can deal with duplication.
4. Container Adapters
The term "container adapters" refers to a group of classes in C++ that offer a particular interface for interacting with the container data structure. They serve as encapsulations for pre-existing container classes and limit the allowed operations to a predetermined set. The C++ Standard Library has three main container adapters, which are as follows:
a) stack
A stack is a Last-In-First-Out (LIFO) interface that is provided by the std::stack container adapter. The operations it exposes are push, pop, top, empty, and size. It uses an underlying container (by default, std::deque).
b) queue
First-In-First-Out (FIFO), or queue, the interface is provided by the std::queue container adapter. Push, pop, front, back, empty, and size operations are also supported, and it uses an underlying container (by default, std::deque).
c) priority_queue
A priority queue is provided by the std::priority_queue container adapter, where elements are sorted using a specified comparator function (by default, it uses the std::less comparator). Push, pop, top, empty, and size operations are supported, and it uses an underlying container (by default, std::vector).
Using these container adapters is demonstrated in the following example:
Example:
#include <iostream>
#include <stack>
#include <queue>
int main()
{
// Stack example
std::stack<int> intStack;
intStack.push(10);
intStack.push(20);
intStack.push(30);
std::cout << "Top element of the stack: " << intStack.top() << std::endl;
intStack.pop();
std::cout << "After pop, top element of the stack: " << intStack.top() << std::endl;
// Queue example
std::queue<std::string> stringQueue;
stringQueue.push("First");
stringQueue.push("Second");
stringQueue.push("Third");
std::cout << "Front element of the queue: " << stringQueue.front() << std::endl;
stringQueue.pop();
std::cout << "After pop, front element of the queue: " << stringQueue.front() << std::endl;
// Priority Queue (min-heap) example
std::priority_queue<int, std::vector<int>, std::greater<int>> intPriorityQueue;
intPriorityQueue.push(50);
intPriorityQueue.push(20);
intPriorityQueue.push(70);
intPriorityQueue.push(10);
std::cout << "Top element of the priority queue: " << intPriorityQueue.top() << std::endl;
intPriorityQueue.pop();
std::cout << "After pop, top element of the priority queue: " << intPriorityQueue.top() << std::endl;
return 0;
}
Output:
Top element of the stack: 30
After pop, top element of the stack: 20
Front element of the queue: First
After pop, front element of the queue: Second
Top element of the priority queue: 10
After pop, top element of the priority queue: 20
Explanation:
In this example, we show how to use std::stack, std::queue, and std::priority_queue in their fundamental actions. We construct instances of each container adapter for push, pop, top, and front operations.