August 10, 2025

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.

YouTube video thumbnail

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 a bool.


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 consider std::unordered_set for raw speed.
Follow me

I work on everything coding and share developer memes