The C++ Standard Template Library (STL) is a powerful set of C++ template classes to provide general-purpose classes and functions with templates that implement many popular and commonly used algorithms and data structures like vectors, lists, queues, and stacks. In this blog, we will explore the fundamentals of the C++ STL and demonstrate its usage with practical examples.
Table of Contents:
-
STL Containers
- Vectors
- Lists
- Maps
- Sets
-
STL Algorithms
- Sorting
- Searching
- Iterating
- Transforming
-
STL Functions
- Lambda Functions
- Function Objects (Functors)
- Function Binders
-
Example Applications
- Sorting a Vector of Integers
- Using Map for Data Storage
- Solving Problems with Algorithms
- Customizing Functionality with Functors
1. STL Containers¶
STL provides various container classes to store and manipulate data efficiently. Let's take a closer look at some commonly used containers.
Vectors¶
Vectors are dynamic arrays that can grow or shrink in size automatically. Here's how you create and use a vector:
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector;
myVector.push_back(1);
myVector.push_back(2);
myVector.push_back(3);
for (int i : myVector) {
std::cout << i << " ";
}
return 0;
}
Lists¶
Lists are doubly-linked lists, allowing for fast insertions and deletions at both ends. Here's a simple example:
#include <iostream>
#include <list>
int main() {
std::list<int> myList;
myList.push_back(1);
myList.push_back(2);
myList.push_front(3);
for (int i : myList) {
std::cout << i << " ";
}
return 0;
}
Maps¶
Maps are associative containers that store key-value pairs. Here's how you can use a map:
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> myMap;
myMap["Alice"] = 25;
myMap["Bob"] = 30;
std::cout << "Alice's age: " << myMap["Alice"] << std::endl;
return 0;
}
Sets¶
Sets are containers that store unique values. They can be handy when you need to eliminate duplicates from a collection:
#include <iostream>
#include <set>
int main() {
std::set<int> mySet;
mySet.insert(1);
mySet.insert(2);
mySet.insert(1); // Duplicate value
for (int i : mySet) {
std::cout << i << " ";
}
return 0;
}
2. STL Algorithms¶
STL provides a wide range of algorithms that work seamlessly with containers. Here are some examples:
Sorting¶
Sorting elements in a container is a common task, and STL provides efficient sorting algorithms:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> myVector = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
std::sort(myVector.begin(), myVector.end());
for (int i : myVector) {
std::cout << i << " ";
}
return 0;
}
Searching¶
STL offers various searching algorithms, such as find
, binary_search
, and count
:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5, 6};
if (std::binary_search(myVector.begin(), myVector.end(), 3)) {
std::cout << "Found 3 in the vector." << std::endl;
} else {
std::cout << "3 not found." << std::endl;
}
return 0;
}
Iterating¶
STL provides iterators to traverse through containers:
#include <iostream>
#include <vector>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
Transforming¶
You can transform elements in a container using various algorithms like transform
:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
std::transform(myVector.begin(), myVector.end(), myVector.begin(),
[](int x) { return x * 2; });
for (int i : myVector) {
std::cout << i << " ";
}
return 0;
}
3. STL Functions¶
STL allows you to work with functions flexibly using concepts like lambda functions, function objects (functors), and function binders.
Lambda Functions¶
Lambda functions are anonymous functions defined on the fly. They are handy for short, one-off operations:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
// Using a lambda function to multiply each element by 2
std::for_each(myVector.begin(), myVector.end(),
[](int &x) { x *= 2; });
for (int i : myVector) {
std::cout << i << " ";
}
return 0;
}
Functors (Function Objects)¶
Functors are objects that can be called as if they were functions. They can have their own state, making them more powerful than simple functions:
#include <iostream>
#include <vector>
#include <algorithm>
// Define a functor
struct AddValue {
int value;
AddValue(int val) : value(val) {}
int operator()(int x) const {
return x + value;
}
};
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
AddValue addFive(5); // Create an instance of the functor
std::transform(myVector.begin(), myVector.end(), myVector.begin(), addFive);
for (int i : myVector) {
std::cout << i << " ";
}
return 0;
}
Function Binders¶
Function bind
ers allow you to fix certain arguments of a function before calling it:
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
int main() {
std::vector<int> myVector = {1, 2, 3, 4, 5};
// Using a function binder to add 5 to each element
std::transform(myVector.begin(), myVector.end(), myVector.begin(),
std::bind(std::plus<int>(), std::placeholders::_1, 5));
for (int i : myVector) {
std::cout << i << " ";
}
return 0;
}
4. Example Applications¶
Now that we've covered the basics of STL containers, algorithms, and functions, let's see how you can apply these concepts to real-world scenarios.
Sorting a Vector of Integers¶
Suppose you have an unsorted vector of integers, and you want to sort it in ascending order:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 1, 4, 2, 3};
// Sort the vector
std::sort(numbers.begin(), numbers.end());
// Print the sorted numbers
for (int num : numbers) {
std::cout << num << " ";
}
return 0;
}
Using Map for Data Storage¶
Imagine you need to store and retrieve data with associated keys. You can use a map for this purpose:
#include <iostream>
#include <map>
int main() {
std::map<std::string, int> scores;
// Insert key-value pairs
scores["Alice"] = 95;
scores["Bob"] = 87;
scores["Charlie"] = 92;
// Retrieve and print a specific score
std::cout << "Charlie's score: " << scores["Charlie"] << std::endl;
return 0;
}
Solving Problems with Algorithms¶
STL algorithms are incredibly versatile. Let's say you want to find the sum of all even numbers in a vector:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// Use std::accumulate with a lambda function
int sum = std::accumulate(numbers.begin(), numbers.end(), 0,
[](int acc, int val) { return val % 2 == 0 ? acc + val : acc; });
std::cout << "Sum of even numbers: " << sum << std::endl;
return 0;
}
Customizing Functionality with Functors¶
Functors can be beneficial when you need to customize functionality in your code. Suppose you want to calculate the sum of squares of a vector of numbers:
#include <iostream>
#include <vector>
#include <algorithm>
struct Square {
int operator()(int x) const {
return x * x;
}
};
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
Square square; // Create an instance of the functor
// Use std::transform with the functor
std::transform(numbers.begin(), numbers.end(), numbers.begin(), square);
for (int num : numbers) {
std::cout << num << " ";
}
return 0;
}