Standard Template Library (STL) in C++

Filed Under: C++
Standard Template Library In C

Standard Template Library (STL) is a collection of standard C++ template classes. It consists of generic methods and classes to work with different forms of data.

Standard Template Library is basically a generic library i.e. a single method/class can operate on different data types. So, as understood, we won’t have to declare and define the same methods/classes for different data types.

Thus, STL saves a lot of effort, reduces the redundancy of the code and therefore, leads to the increased optimization of the code blocks.


Components of Standard Template Library

The following are the basic components provided by the C++ Standard Template Library:

  • Containers
  • Algorithms
  • Iterator

1. Containers in Standard Template Library

In Standard Template Library, Containers create and store data and objects. Containers create generic data structures that can hold values of different data types altogether.

Following are the different types of Containers in STL:

  • Sequence Containers
  • Associative Containers
  • Container Adapters
  • Unordered Associative Containers

Sequence Containers in Standard Template Library

Sequential Containers basically inculcate and implement the classes/functions containing the data structures wherein the elements can be accessed in a sequential manner.

The Sequential Containers consists of the following commonly used data structures:

  • Array
  • Vector
  • List
  • deque, etc

Let’s dive into the working of some of the data structures offered by Sequential Containers.


Arrays

The Array class of Sequential Containers is much more efficient than the default array structure. Moreover, elements in an array are accessible in a sequential manner.

Syntax:

array<data_type, size> = {value1, value2, ...., valueN};

The Array class includes a huge count of functions to manipulate the data.

Some of the most commonly used functions of the generic Array class is as follows:

  • array.front(): It is used to access and print the first element of the array.
  • array.back(): It is used to access and print the last element of the array.
  • array.empty(): Checks whether the array is empty or not.
  • array.at(): Access and print the elements of the array.
  • array.swap(): Function to swap two input arrays.
  • array.fill(): It fills the array with a specific/particular input element.

Let’s go through an example to understand the working of some basic yet common functionalities served by the Array class of Sequential Containers.

Example:

#include<iostream> 
#include<array> 
#include<tuple> 
using namespace std; 
int main() 
{ 
	
	array<int,4> arr = {20, 150, 0, -11}; 

	
	cout << "Displaying the array elements using at():\n"; 
	for ( int i=0; i<4; i++) 
	cout << arr.at(i) << " "; 
	cout << endl; 

	
	cout << "Displaying the array elements using get():\n"; 
	cout << get<0>(arr)<<"\n"; 
	cout << get<2>(arr);
	cout << endl; 

	cout << "First element of the array:\n"; 
    cout << arr.front()<<endl; 
  
    
    cout << "Last element of the array:\n"; 
    cout << arr.back()<<endl;
    
    
	return 0; 
} 

In the above example, #include<array> is used to access the at() function of Array class. The at() function is used to access the elements of the input array by traversing through a for loop.

Further, we can even use the get() method of class tuple to access the elements by overloading the function.

The statement get<0>(arr) enables to access and print the element at position 0 (zero) of the array.

Output:

Displaying the array elements using at():
20 150 0 -11 
Displaying the array elements using get():
20
0
First element of the array:
20
Last element of the array:
-11

List

The List class of the Sequential Containers provides storage to elements with non-contagious memory locations.

Syntax:

list <data_type> list_name

Some of the most commonly used functions provided by the List class are:

  • list.begin(): It returns an iterator element that points to the first element of the list.
  • list.end(): It returns an iterator element that points to the last element of the list.
  • list.reverse(): It reverses the input list.
  • list.sort(): This function is used to sort the input list.
  • list.push_front(element): It inserts an element to the beginning of the list.
  • list.push_back(element): It inserts an element to the end of the list.
  • list.front(): It returns the first element of the list
  • list.back(): It returns the last element of the list.
  • list.pop_front(): This function deletes the first element of the list and decreases the size of the list by 1.
  • list.pop_back(): This function deletes the last element of the list and decreases the size of the list by 1.

Example:

#include <iostream> 
#include <list> 
#include <iterator> 
using namespace std; 


void display(list <int> l) 
{ 
	list <int> :: iterator i; 
	for(i = l.begin(); i != l.end(); ++i) 
		cout << '\t' << *i; 
	cout << '\n'; 
} 

int main() 
{ 

	list <int> lst1, lst2; 


	for (int i = 0; i < 5; ++i) 
	{ 
		lst1.push_front(i); 
	
	} 
	cout <<"\nList 1:\n"; 
	display(lst1); 

	cout << "\nUsing list.front() function:\n " << lst1.front(); 
	cout << "\nUsing list.back() function:\n " << lst1.back();
	
	cout << "\nUsing list.pop_back() function:\n "; 
	lst1.pop_back(); 
	display(lst1); 

	cout << "\nUsing list.pop_front() function:\n "; 
	lst1.pop_front(); 
	display(lst1); 

	
    cout << "\nThe list.reverse() function:\n "; 
	lst1.reverse(); 
	display(lst1); 

	cout << "\nThe list.sort() function:\n"; 
	lst1.sort(); 
	display(lst1); 

	return 0; 

} 

In the above snippet of code, we have used an Iterator to traverse the list efficiently. We will get to learn more about Iterators in this article.

Output:

List 1:
4	3	2	1	0

Using list.front() function:
 4
Using list.back() function:
 0
Using list.pop_back() function:
 4	3	2	1

Using list.pop_front() function:
 3	2	1

The list.reverse() function:
 1	2	3

The list.sort() function:
1	2	3

Vector

Vectors in C++ function the same way as Arrays in a dynamic manner i.e. vectors can resize itself automatically whenever an item is added/deleted from it.

The data elements in Vectors are placed in contagious memory locations and Iterator can be easily used to access those elements. Moreover, insertion of items in takes place at the end of Vector.

Syntax:

vector<data_type> vector_name;

Most commonly used functions of Vector:

  • vector.begin(): It returns an iterator element which points to the first element of the vector.
  • vector.end(): It returns an iterator element which points to the last element of the vector.
  • vector.push_back(): It inserts the element into the vector from the end.
  • vector.pop_back(): It deletes the element from the end of the vector.
  • vector.size(): This function gives the size i.e. the number of elements in the vector.
  • vector.empty(): Checks whether the vector is empty or not.
  • vector.front(): It returns the first element of the vector.
  • vector.back(): It returns the last element of the vector.
  • vector.insert(): This function adds the element before the element at the given location/position.
  • vector.swap(): Swaps the two input vectors.
#include <iostream> 
#include <vector> 

using namespace std; 

int main() 
{ 
	vector<int> V1; 

	for (int i = 1; i <= 4; i++) 
		V1.push_back(i); 

	cout << "Displaying elements of vector using begin() and end():\n"; 
	for (auto i = V1.begin(); i != V1.end(); ++i) 
		cout << *i << " "; 
	
	
    cout << "\nSize of the input Vector:\n" << V1.size();	
		
	if (V1.empty() == false) 
            cout << "\nVector isn't empty"; 
    else
            cout << "\nVector is empty"; 
        
    cout << "\nvector.front() function:\n" << V1.front(); 
  
    cout << "\nvector.back() function:\n" << V1.back(); 
      
    V1.insert(V1.begin(), 8); 
    cout<<"\nVector elements after the insertion of element using vector.insert() function:\n";
    
    for (auto x = V1.begin(); x != V1.end(); ++x) 
		cout << *x << " "; 

    return 0; 
} 

In the above snippet of code, the statement V1.insert(V1.begin(), 8) inserts the element (8) at the beginning i.e. before the first element of the vector.

Output:

Displaying elements of vector using begin() and end():
1 2 3 4 
Size of the input Vector:
4
Vector isn't empty
vector.front() function:
1
vector.back() function:
4
Vector elements after the insertion of element using vector.insert() function:
8 1 2 3 4 

Associative Containers in Standard Template Library

Associated Containers implement and inculcate generic classes and functions with sorted data structures which in turns leads to reduced time complexity.

The following are the data structures offered by the Associative Containers:

  • Set
  • Map
  • multiset
  • multimap

Set

Sets in Associative Containers stores unique elements i.e. redundant elements are not allowed. Moreover, once the element is inserted into the set, it cannot be altered or modified.

Syntax:

set <data_type> set_name;

Some of the commonly used functions offered by set:

  • set.insert(value): This function inserts the element to the existing set.
  • set.begin(): It returns an iterator element that points to the first element of the set.
  • set.end(): It returns an iterator element that points to the last element of the set.
  • set.erase(position): It removes the element from the position or element entered.
  • set.size(): It returns the number of elements contained in a particular set.

Example:

#include <iostream> 
#include <set> 
#include <iterator> 

using namespace std; 

int main() 
{ 
	
	set <int> S;		 
    int i=1;
	while(i<5)
	{
	    S.insert(i*10);
	    i++;
	}
	
	set <int> :: iterator it; 
	cout << "\nInput set:\n"; 
	for (it = S.begin(); it != S.end(); ++it) 
	{ 
		cout << '\t' << *it; 
	} 
	cout << endl; 
	
	int num;
	
	num = S.erase (10); 
    cout<<"Set after removal of element using erase() function:\n";
	for (it = S.begin(); it != S.end(); ++it) 
	{ 
		cout << '\t' << *it; 
	} 

	cout << endl; 

	return 0; 

} 

Output:

Input set:
10	20	30	40
Set after removal of element using erase() function:
20	30	40

Multiset

Multisets serve the same functionality as that of Sets. The only difference is that Multisets can have duplicate elements in it i.e. it allows redundancy.

Syntax:

multiset<data_type> multiset_name;

Example:

#include <iostream> 
#include <set> 
#include <iterator> 

using namespace std; 

int main() 
{ 
	
	multiset <int> S;		 
    int i=1;
	while(i<5)
	{
	    S.insert(i*10);
	    i++;
	}
	S.insert(70);
	S.insert(70);
	
	multiset <int> :: iterator it; 
	cout << "\nElements of the set:\n"; 
	for (it = S.begin(); it != S.end(); ++it) 
	{ 
		cout << '\t' << *it; 
	} 
	cout << endl; 

	
	int x;
	
	x = S.erase (10); 
    cout<<"Set after using erase() function to delete an element:\n";
	for (it = S.begin(); it != S.end(); ++it) 
	{ 
		cout << '\t' << *it; 
	} 

	cout << endl; 

	return 0; 

} 

Output:

Elements of the set:
	10	20	30	40	70	70
Set after using erase() function to delete an element:
	20	30	40	70	70

Map

Maps store elements in a key-value pair. Each value is mapped with a key unique element.

Syntax:

map<data_type of key, data_type of element> map_name; 

Some of the commonly used functions offered by Maps:

  • map.insert(): This function inserts the element with a particular/specific key to the existing map.
  • map.begin(): It returns an iterator element that points to the first element of the map.
  • map.end(): It returns an iterator element that points to the last element of the map.
  • map.size(): It returns the number of key-value pairs in the map.
  • map.empty(): It checks whether the map is empty or not.

Example:

#include <iostream> 
#include <iterator> 
#include <map> 

using namespace std; 

int main() 
{ 


	map<int, int> M; 

	
    M.insert(pair<int, int>(1, 100)); 
	M.insert(pair<int, int>(2, 70)); 
	M.insert(pair<int, int>(3, 90)); 
	M.insert(pair<int, int>(4, 20)); 



	map<int, int>::iterator itr; 
	cout << "\nInput Map:\n"; 
	cout << "key:value\n"; 
	for (itr = M.begin(); itr != M.end(); ++itr) { 
		cout << itr->first 
			<< ':' << itr->second << '\n'; 
	} 
	cout << endl; 



	cout << "\nMap after removal of elements: \n"; 
		cout << "key:value\n"; 
	M.erase(M.begin(), M.find(3)); 
	for (itr = M.begin(); itr != M.end(); ++itr) { 
		cout << itr->first 
			<< ':' << itr->second << '\n'; 
	} 

	
	return 0; 
} 

Output:

Input Map:
key:value
1:100
2:70
3:90
4:20


Map after removal of elements: 
key:value
3:90
4:20

Multimap

Multimaps function the same way as Maps, but the only difference is that in Multimaps, redundant key values are allowed i.e. the elements can have the same key values.

Syntax:

multimap<data_type of key, data_type of element> multimap_name; 

Example:

#include <iostream> 
#include <iterator> 
#include <map> 

using namespace std; 

int main() 
{ 


	multimap<int, int> M; 

	
    M.insert(pair<int, int>(1, 100)); 
	M.insert(pair<int, int>(1, 70)); 
	M.insert(pair<int, int>(3, 90)); 
	M.insert(pair<int, int>(4, 20)); 



	multimap<int, int>::iterator itr; 
	cout << "\nInput Map:\n"; 
	cout << "key:value\n"; 
	for (itr = M.begin(); itr != M.end(); ++itr) { 
		cout << itr->first 
			<< ':' << itr->second << '\n'; 
	} 
	cout << endl; 



	cout << "\nMap after removal of elements: \n"; 
		cout << "key:value\n"; 
	M.erase(M.begin(), M.find(3)); 
	for (itr = M.begin(); itr != M.end(); ++itr) { 
		cout << itr->first 
			<< ':' << itr->second << '\n'; 
	} 

	
	return 0; 
} 

Output:

Input Map:
key:value
1:100
1:70
3:90
4:20


Map after removal of elements: 
key:value
3:90
4:20

As you can see in the above example, the first two key:value pairs have the same key. If we try the same in with “map” instead of “multimap”, only the first element with the unique key will be used while the other is dropped.


Container Adapters

Container Adapters provide a different interface to the Sequential Adapters.

Some of the commonly used data structures provided by Container Adapters are:

  • queue
  • priority queue
  • stack

Queue

Queues work in First-In-First-Out(FIFO) fashion. The items are inserted from the rear i.e from the end and are deleted from the front.

Syntax:

queue <data_type> queue_name;

Some of the commonly used functions offered by generic Queue:

  • queue.empty(): Checks whether the queue is empty or not.
  • queue.push(element): This functions adds an element to the end of the queue.
  • queue.pop(): It deletes the first element of the queue.
  • queue.front(): It returns an iterator element which points to the first element of the queue.
  • queue.back(): It returns an iterator element which points to the last element of the queue.

Example:

#include <iostream> 
#include <queue> 

using namespace std; 

void display(queue <int> Q1) 
{ 
	queue <int> Q = Q1; 
	while (!Q.empty()) 
	{ 
		cout << '\t' << Q.front(); 
		Q.pop(); 
	} 
	cout << '\n'; 
} 

int main() 
{ 
    int i=1;
	queue <int> qd; 
    while (i<5)
    {
        qd.push(i);
        i++;
    }
	

	cout << "Queue:\n"; 
	display(qd);
	cout<<"Popping an element from the queue..\n";
    qd.pop();
    display(qd);
	return 0; 
} 

Output:

Queue:
1	2	3	4
Popping an element from the queue..
2	3	4


Priority queue

In priority queue, the elements are placed in decreasing order of their values and the first element represents the largest of all the inserted elements.

Syntax:

priority_queue <data_type> priority_queue_name;

Some of the functions offered by Priority queue:

  • priority_queue.empty(): Checks whether the queue is empty or not.
  • priority_queue.top(): It returns the top element from the queue.
  • priority_queue.pop(): It deletes the first element from the queue
  • priority_queue.push(element): It inserts the element at the end of the queue.
  • priority_queue.swap(): It swaps the elements of one priority queue with another of the similar data type and size.
  • priority_queue.size(): It returns the number of elements present in the priority queue.

Example:

#include <iostream> 
#include <queue> 

using namespace std; 

void display(priority_queue <int> PQ) 
{ 
	priority_queue <int> p = PQ; 
	while (!p.empty()) 
	{ 
		cout << '\t' << p.top(); 
		p.pop(); 
	} 
	cout << '\n'; 
} 

int main () 
{    int i=1;
	priority_queue <int> PQ; 
	while(i<5)
	{
	    PQ.push(i*10);
	    i++;
	}
	

	cout << "The priority queue:\n"; 
	display(PQ); 

	 
	cout << "\nThe priority_queue.top() function:\n" << PQ.top(); 


	cout << "\nThe priority_queue.pop() function:\n"; 
	PQ.pop(); 
	display(PQ); 

	return 0; 
} 

Output:

The priority queue:
40	30	20	10
The priority_queue.top() function:
40
The priority_queue.pop() function:
30	20	10

Stack

Stack follows the Last-In-First-Out (LIFO) fashion. The items are inserted at one end of the stack and an item is deleted from the same end of the stack.

Syntax:

stack <data_type> stack_name;

Functionalities served by Stack are as follows:

  • stack.push(element): It inserts an element to the top of the stack.
  • stack.pop(): It deletes an element present at the top of the stack.
  • stack.empty(): It checks whether the stack is empty or not.
  • stack.top(): It returns the element present at the top of the stack.

Example:

#include <bits/stdc++.h> 
using namespace std; 

void display(stack <int> S) 
{ 
	while (!S.empty()) 
	{ 
		cout << '\t' << S.top(); 
		S.pop(); 
	} 
	cout << '\n'; 
} 

int main () 
{ 
	stack <int> s; 
	s.push(1); 
	s.push(0); 
	s.push(2); 
	s.push(6); 
	

	cout << "The stack is : "; 
	display(s); 

 
	cout << "\nThe top element of the stack:\n" << s.top(); 


	cout << "\nStack after removing the top element from the stack:\n"; 
	s.pop(); 
	display(s); 

	return 0; 
} 

Output:

The stack is : 	6	2	0	1
The top element of the stack:
6
Stack after removing the top element from the stack:
2	0	1

Unordered Associative Containers

Unordered Associative Containers provides sorted and unordered data structures that can be used for efficient searching operations.

The following data structures are provided by the Unordered Associative Containers:

  • unordered_set: A hash table is used to build and implement the unordered_set wherein the keys are hashed into the indices in order to randomize the insertion operation. The keys can be stored in any random order in an unordered_set.
  • unordered_multiset: It works in a similar fashion as that of the unordered_set. In unordered_multiset, duplicate items can be stored.
  • unordered_map: It works like a map data structure i.e. has key-value pair associated with it. In unordered_map, the keys can be stored in any random order.
  • unordered_multimap: It works like multimap, but does allow storage of the duplicate key-value pairs.

2. Algorithms in Standard Template Library

Standard Template Library provides us with different generic algorithms. These algorithms contain built in generic functions which can be directly accessed in the program.

The Algorithm and its functions can be accessed with the help of Iterators only.

Types of Algorithms offered by Standard Template Library:

  • Sorting Algorithms
  • Search algorithms
  • Non-modifying algorithms
  • Modifying algorithms
  • Numeric algorithms
  • Minimum and Maximum operations

Sorting Algorithm in Standard Template Library

Standard Template Library provides built-in sort() method to sort the elements in a particular data structure.

Internally, it uses a combination of Quick Sort, Heap Sort, and Insertion Sort to sort the elements.

Syntax:

sort(starting index, end_element_index)

Example:

#include <iostream> 
#include <algorithm> 

using namespace std; 

int main() 
{ 
	int arr[5]= {10, 50, 0, -1, 48};
	cout << "\nArray before sorting:\n"; 
	
	for(int i = 0; i < 5; ++i) 
		cout << arr[i] << " ";  

	sort(arr, arr+5); 

	cout << "\nArray after sorting:\n"; 
	for(int i = 0; i < 5; ++i) 
		cout << arr[i] << " ";  

	return 0; 

} 

Output:

Array before sorting:
10 50 0 -1 48 
Array after sorting:
-1 0 10 48 50 

3. Iterators in Standard Template Library

Iterators are basically used to point or refer to a particular memory location of the element. They point at the containers and help them manipulate the elements in an efficient manner.

Following are some of the commonly used functions offered by Iterators in Standard Template Library:

  • iterator.begin(): It returns the reference to the first element of the container.
  • iterator.end(): It returns the reference to the last element of the container.
  • iterator.prev(iterator_name, index): It returns the position of the new iterator that would point to the element before the specified index value in the parameter.
  • iterator.next(iterator_name, index): It returns the position of the new iterator that would point to the element after the specified index value in the parameter.
  • iterator.advance(index): It increments the iterator’s position to the specified index value.

Example:

#include<iostream> 
#include<iterator> 
#include<list> 
using namespace std; 
int main() 
{ 
	list<int> lst = {10, 50, 70, 45, 36}; 
	
	list<int>::iterator itr; 
	
	cout << "\nInput List:\n"; 
	for (itr = lst.begin(); itr != lst.end(); itr++) 
		cout << *itr << " "; 
		
    list<int>::iterator it = lst.begin(); 
	advance(it,1); 
      
    cout << "\nThe position of iterator after advance():\n"; 
    cout << *it << " "; 
      	
	
	return 0;	 
} 

In the above snippet of code, advance(it, 1) points the iterator to the element at the index value = 1.

Output:

Input List:
10 50 70 45 36 
The position of iterator after advancing is : 
50 

Conclusion

In this article, we have understood the Standard Template Library and it components in a detailed manner.


References

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages