Back to C++
2026-03-148 min read

Replacement functions (C++)

Learn Replacement functions (C++) step by step with clear examples and exercises.

Why This Matters

Replacement functions in C++ are crucial for creating more efficient and adaptable code. They enable you to customize the behavior of standard library functions like std::less, std::greater, and std::hash by providing your own comparison, hash, or other types of functions. By understanding and mastering replacement functions, you can tailor the performance and functionality of your programs to meet specific requirements.

Prerequisites

To fully grasp the concept of replacement functions in C++, it is essential to have a strong foundation in several areas:

  1. Basic C++ syntax and control structures (loops, conditionals)
  2. Classes and Objects
  3. Function overloading
  4. Operator Overloading
  5. The Standard Template Library (STL)
  6. Understanding of data structures like sets, maps, and unordered containers
  7. Knowledge of algorithms like std::sort, std::min_element, etc.

A good understanding of these topics will help you navigate the complexities of replacement functions and apply them effectively in your C++ programs.

Core Concept

Replacement functions in C++ are specialized template functions that empower you to tailor the behavior of standard library algorithms by providing your own comparison, hash, or other types of functions. They are defined using the template<> keyword and are commonly used with STL containers such as std::set, std::map, and std::unordered_map.

Custom Comparison Functions (Expanded)

Let's start by creating a custom comparison function for a simple Student class. The default comparison function provided by the standard library compares students based on their memory addresses, which is not what we want.

#include <iostream>
#include <set>
#include <algorithm> // For std::greater and std::less
using namespace std;

struct Student {
string name;
int age;

// Overloading the less-than operator (<) to define custom comparison
bool operator<(const Student& other) const {
if (age == other.age)
return name < other.name;
return age < other.age;
}

// Overloading the greater-than operator (>) for use with std::greater
friend bool operator>(const Student& lhs, const Student& rhs) {
return !lhs < rhs;
}
};

// Custom comparison function for a `Person` class that compares people based on their ages.
struct Person {
string name;
int age;

bool operator<(const Person& other) const {
return age < other.age;
}
};

int main() {
set<Student, greater<Student>> students; // Use greater to sort in descending order

// Adding students to the set using the custom comparison function
students.insert(Student{"Alice", 20});
students.insert(Student{"Bob", 19});
students.insert(Student{"Charlie", 21});

for (const auto& student : students) {
cout << student.name << ", " << student.age << "\n";
}

// Using std::greater to sort in descending order and find the minimum age
Student min_age = *min_element(students.begin(), students.end());
cout << "Minimum Age: " << min_age.age << "\n";

// Custom comparison function for a `Person` class
set<Person> people;
people.insert(Person{"Alice", 20});
people.insert(Person{"Bob", 19});
people.insert(Person{"Charlie", 21});

// Iterating through the sorted `people` set
for (const auto& person : people) {
cout << person.name << ", " << person.age << "\n";
}

return 0;
}

In this example, we overload the less-than operator (<) and the greater-than operator (>) to define a custom comparison function for our Student class. We also create a custom comparison function for the Person class that compares people based on their ages. This allows us to compare students and people based on their names and ages instead of their memory addresses. When we insert students or people into the sets, the custom comparison functions are automatically used.

Custom Hash Functions (Expanded)

Custom hash functions are crucial when working with unordered containers like std::unordered_map or std::unordered_set. By providing a custom hash function, you can control how objects are hashed and ensure better performance for your data structures.

#include <iostream>
#include <unordered_map>
#include <hash>
using namespace std;

struct Student {
string name;
int age;

// Custom hash function for the Student class
size_t hash_value() const {
return hash<string>()(name) ^ (age << 1);
}
};

// Custom hash function for a `RationalNumber` class that represents fractions.
struct RationalNumber {
int numerator;
int denominator;

size_t hash_value() const {
return hash<int>()(numerator) ^ (denominator << 1);
}
};

int main() {
unordered_map<Student, int> students;

// Adding students to the map using the custom hash function
students[Student{"Alice", 20}] = 1;
students[Student{"Bob", 19}] = 2;
students[Student{"Charlie", 21}] = 3;

// Accessing and printing the values for each student
for (const auto& pair : students) {
cout << pair.first.name << ", " << pair.first.age << ": " << pair.second << "\n";
}

unordered_map<RationalNumber, int> rationalNumbers;

// Adding rational numbers to the map using the custom hash function
rationalNumbers[RationalNumber{1, 2}] = 1;
rationalNumbers[RationalNumber{3, -4}] = 2;
rationalNumbers[RationalNumber{-1, 0}] = 3;

// Iterating through the map and printing the values
for (const auto& pair : rationalNumbers) {
cout << pair.first.numerator << "/" << pair.first.denominator << ": " << pair.second << "\n";
}

return 0;
}

In this example, we define a custom hash function for the Student class that combines the hash value of the name with the age shifted left by one bit. We also create a custom hash function for the RationalNumber class that combines the numerator and denominator using a simple multiplication. This allows us to create unordered_maps where students or rational numbers are hashed based on their names, ages, or fractions.

Worked Example

In this example, we will implement a custom comparison function for a ComplexNumber class and use it with a std::set.

#include <iostream>
#include <set>
using namespace std;

struct ComplexNumber {
double real;
double imag;

// Overloading the less-than operator (<) to define custom comparison
bool operator<(const ComplexNumber& other) const {
if (real == other.real)
return imag < other.imag;
return real < other.real;
}
};

// Custom comparison function for a `Point` class that compares points based on their distances from the origin (0, 0).
struct Point {
double x;
double y;

// Overloading the less-than operator (<) to define custom comparison
bool operator<(const Point& other) const {
return sqrt(x * x + y * y) < sqrt(other.x * other.x + other.y * other.y);
}
};

int main() {
set<ComplexNumber> complexNumbers;

// Adding complex numbers to the set using the custom comparison function
complexNumbers.insert(ComplexNumber{1, 2});
complexNumbers.insert(ComplexNumber{3, -4});
complexNumbers.insert(ComplexNumber{-1, 0});

for (const auto& num : complexNumbers) {
cout << num.real << " + " << num.imag << "i\n";
}

// Custom comparison function for a `Point` class
set<Point> points;
points.insert(Point{3, 4});
points.insert(Point{-1, -2});
points.insert(Point{0, 0});

// Iterating through the sorted `points` set
for (const auto& point : points) {
cout << point.x << ", " << point.y << "\n";
}

return 0;
}

In this example, we overload the less-than operator (<) to define a custom comparison function for our ComplexNumber class and create a custom comparison function for the Point class that compares points based on their distances from the origin. When we insert complex numbers or points into the sets, the custom comparison functions are automatically used.

Common Mistakes

  1. Forgetting to overload all comparison operators: When defining a custom comparison function for a class, it's essential to overload both ``. Otherwise, you may encounter unexpected behavior when using your class with STL containers.
  2. Not properly implementing the custom comparison function: Ensure that your custom comparison function follows the same rules as the standard library's comparison functions. It should be a strict weak ordering (SWO), meaning it should be reflexive, transitive, and total.
  3. Using the wrong operator for custom comparison: Be aware of which operator you are overloading. For example, if you want to compare objects based on their memory addresses, use == instead of <.
  4. Not properly implementing the custom hash function: When defining a custom hash function, make sure it follows the requirements for a good hash function, such as being deterministic, uniformly distributed, and fast.
  5. Forgetting to include necessary headers: Don't forget to include the header files required for using STL containers and algorithms, like `, , or `.
  6. Incorrect use of replacement functions with custom allocators: When using custom allocators with STL containers, ensure that you provide replacement functions for the necessary allocation and deallocation operations.
  7. Not considering edge cases: Be mindful of edge cases when implementing replacement functions to ensure they work correctly in all situations.
  8. Inconsistent naming conventions: Follow consistent naming conventions for your custom comparison and hash functions to make your code more readable and maintainable.

Practice Questions

  1. Implement a custom comparison function for a Person class that compares people based on their ages. Use this function with a std::set.
  2. Create a custom hash function for a RationalNumber class that represents fractions. The hash function should combine the numerator and denominator using a simple multiplication.
  3. Implement a custom comparison function for a Point class that compares points based on their distances from the origin (0, 0). Use this function with a std::priority_queue.
  4. Modify the ComplexNumber example to use a custom allocator and deallocator for the set.
  5. Implement a custom comparison function for a Circle class that compares circles based on their areas. Use this function with a std::multiset.
  6. Create a custom hash function for a String class that represents strings with case-insensitive comparison. Use this function with an std::unordered_map.
  7. Implement a custom comparison function for a Triangle class that compares triangles based on their perimeters. Use this function with a std::priority_queue.
  8. Modify the Point example to use a custom allocator and deallocator for the set.
  9. Create a custom hash function for a Date class that represents dates in the format YYYY-MM-DD. Use this function with an std::unordered_map.
  10. Implement a custom comparison function for a Shape class that compares shapes based on their areas or perimeters (depending on the specific shape). Use this function with a std::priority_queue.

FAQ

A: Replacement functions allow you to customize the behavior of standard library functions and data structures, making your code more efficient and flexible. They can help optimize performance, handle specific data types, and provide more control over the order of elements in containers.

Q: How do I define a custom comparison function for a class in C++?

A: To define

Replacement functions (C++) | C++ | XQA Learn