Mastering std::set in C++
Posted on August 10, 2025 • 5 minutes • 1025 words
The std::set
container in C++ is part of the Standard Template Library (STL) and is used to store unique elements in a sorted order.
It’s a powerful associative container when you need quick lookups, automatic sorting, and no duplicate values.

Key Characteristics
- Stores only unique elements (duplicates are not allowed).
- Sorted order of elements (by default, ascending).
- Fast search, insertion, and deletion — average O(log n) complexity.
- Implemented as a balanced binary search tree (usually a Red-Black tree).
- Supports custom sorting via comparators.
Syntax
#include <set>
// Creates an empty set of integers
std::set<int> s;
Basic Operations
1. Insertion
#include <iostream>
#include <set>
int main()
{
std::set<int> s;
s.insert(10);
s.insert(20);
s.insert(5);
s.insert(10); // Duplicate, will be ignored
for (int x : s)
{
std::cout << x << " ";
}
}
Output:
5 10 20
The set automatically sorted the values and ignored the duplicate 10
.
2. Searching
#include <set>
#include <iostream>
int main()
{
std::set<int> s = {1, 3, 5, 7};
if (s.find(5) != s.end())
{
std::cout << "Found\n";
} else
{
std::cout << "Not found\n";
}
}
find()
returns an iterator to the element if found, otherwise end()
.
Note: Since C++20 there’s also
contains(value)
which returns abool
.
3. Deletion
// Remove by value (returns number of erased elements)
s.erase(10);
// Remove by iterator
s.erase(s.begin());
4. Size and Empty Check
std::cout << "Size: " << s.size() << "\n";
if (s.empty())
{
std::cout << "Set is empty\n";
}
Iterating Over a Set
for (auto it = s.begin(); it != s.end(); ++it)
{
std::cout << *it << " ";
}
Using range-based loop:
for (int val : s)
{
std::cout << val << " ";
}
Reverse iteration:
for (auto it = s.rbegin(); it != s.rend(); ++it)
{
std::cout << *it << " ";
}
Custom Sorting
Descending order
std::set<int, std::greater<int>> s;
s.insert({1, 4, 2});
Custom comparator
struct CustomCompare
{
bool operator()(const std::string &a, const std::string &b) const
{
return a.length() < b.length();
}
};
std::set<std::string, CustomCompare> mySet = {"apple", "banana", "kiwi"};
Common Member Functions
Function | Description |
---|---|
insert(value) |
Inserts an element (if not already present). |
emplace(args...) |
Constructs element in-place if not present. |
find(value) |
Finds an element and returns iterator or end() . |
count(value) |
Returns 1 if value exists, else 0 . |
erase(value) |
Removes by value. |
erase(iterator) |
Removes by iterator. |
clear() |
Removes all elements. |
size() |
Number of elements. |
empty() |
Checks if empty. |
lower_bound(val) |
Iterator to first element >= val. |
upper_bound(val) |
Iterator to first element > val. |
equal_range(val) |
Pair of iterators [lower_bound, upper_bound) . |
swap(other_set) |
Exchanges contents with other_set . |
merge(other) |
(C++17) Moves elements from other that don’t conflict. |
extract() / insert(node) |
(C++17) Transfer node handles between containers. |
Examples
Example: Remove duplicates (convert vector → set)
#include <iostream>
#include <set>
#include <vector>
int main()
{
std::vector<int> nums = {4, 2, 4, 5, 2, 3};
std::set<int> uniqueNums(nums.begin(), nums.end());
for (int num : uniqueNums)
{
std::cout << num << " ";
}
}
Output:
2 3 4 5
Example: Unique words from text
#include <iostream>
#include <set>
#include <sstream>
#include <string>
int main()
{
std::string text = "apple banana apple orange banana";
std::stringstream ss(text);
std::set<std::string> uniqueWords;
std::string word;
while (ss >> word)
{
uniqueWords.insert(word);
}
for (auto &w : uniqueWords)
{
std::cout << w << "\n";
}
}
Example: lower_bound, upper_bound, equal_range
#include <iostream>
#include <set>
int main()
{
std::set<int> s = {1, 3, 5, 7, 9};
auto lb = s.lower_bound(4); // points to 5
if (lb != s.end())
std::cout << "lower_bound(4) = " << *lb << "\n";
auto ub = s.upper_bound(5); // points to 7
if (ub != s.end())
std::cout << "upper_bound(5) = " << *ub << "\n";
auto er = s.equal_range(5); // pair (iterator to 5, iterator to 7)
if (er.first != s.end())
std::cout << "*equal_range.first = " << *er.first << "\n";
}
Example: Set operations using (union, intersection, difference)
#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::set<int> a = {1,2,3,4};
std::set<int> b = {3,4,5,6};
std::vector<int> result;
// Intersection
std::set_intersection(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(result));
std::cout << "Intersection: ";
for (int x : result)
std::cout << x << " ";
std::cout << "\n";
result.clear();
// Union
std::set_union(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(result));
std::cout << "Union: ";
for (int x : result)
std::cout << x << " ";
std::cout << "\n";
result.clear();
// Difference a - b
std::set_difference(a.begin(), a.end(), b.begin(), b.end(),
std::back_inserter(result));
std::cout << "Difference (a - b): ";
for (int x : result)
std::cout << x << " ";
std::cout << "\n";
}
Advanced Usage (C++17 and later)
emplace
and emplace_hint
s.emplace(42); // construct in-place
s.emplace_hint(s.begin(), 7); // hint for faster insertion if position known
merge
(C++17)
std::set<int> s1 = {1,2,3};
std::set<int> s2 = {3,4,5};
s1.merge(s2); // s1: {1,2,3,4,5}, s2 contains only elements that collided (if any)
Node handle (extract
/ insert
) (C++17)
auto nh = s1.extract(2);
s2.insert(std::move(nh)); // transfer element without reallocation of key
contains
(C++20)
if (s.contains(3))
{
// found
}
Performance Notes
find
,insert
,erase
— average O(log n) (tree-based).std::unordered_set
gives average O(1) lookup/insert/erase (hash-based) but no ordering.- Use
std::set
when you need automatic ordering and unique elements, and can accept logarithmic operation costs. - For small sizes, overhead of tree structure may be higher than vector/other containers — measure if performance-critical.
When to choose std::set
vs std::unordered_set
-
Choose
std::set
if:- You need elements sorted.
- You need order-dependent operations (
lower_bound
, range queries). - You want deterministic ordering across runs.
-
Choose
std::unordered_set
if:- You only need uniqueness and fast average-time lookup.
- Order is irrelevant.
Summary
std::set
stores unique, ordered elements with logarithmic-time basic operations.- Supports custom comparators and many useful member functions for searching and range queries.
- Modern C++ added powerful node/merge features (C++17) and
contains
(C++20). - Use
std::set
when ordering + uniqueness are needed; otherwise considerstd::unordered_set
for raw speed.