Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Comparison Operator Overloading
Implement ==, !=, <, >, <=, >= for type comparisons and sorting.
Overloading the comparison operators
Comparison operators allow us to compare objects of our classes just like we compare fundamental types. Since comparison operators don't modify their operands, we typically implement them as friend functions.
Overloading operator== and operator!=
Let's start with a Version class that compares software versions:
#include <iostream>
class Version
{
private:
int m_major{};
int m_minor{};
int m_patch{};
public:
Version(int major, int minor = 0, int patch = 0)
: m_major{major}, m_minor{minor}, m_patch{patch}
{
}
friend bool operator==(const Version& v1, const Version& v2);
friend bool operator!=(const Version& v1, const Version& v2);
};
bool operator==(const Version& v1, const Version& v2)
{
return v1.m_major == v2.m_major &&
v1.m_minor == v2.m_minor &&
v1.m_patch == v2.m_patch;
}
bool operator!=(const Version& v1, const Version& v2)
{
return !(v1 == v2);
}
int main()
{
Version current{2, 1, 0};
Version latest{2, 1, 0};
Version old{1, 5, 3};
if (current == latest)
std::cout << "You have the latest version\n";
if (current != old)
std::cout << "There's a newer version available\n";
return 0;
}
When comparison doesn't make sense
Not every class benefits from comparison operators. Consider what it would mean for one Version to be "less than" another. Should we compare major first? Is 1.9.0 less than 2.0.0? What about 1.10.0 vs 1.9.0? While version comparison has established semantics, other classes may not have obvious ordering.
Only define overloaded operators that have intuitive, unambiguous meanings for your class.
Overloading all six comparison operators
When comparison does make sense, let's see how to implement all six operators. Here's a Priority class for task scheduling:
#include <iostream>
class Priority
{
private:
int m_level{};
public:
explicit Priority(int level) : m_level{level} {}
friend bool operator==(const Priority& p1, const Priority& p2);
friend bool operator!=(const Priority& p1, const Priority& p2);
friend bool operator<(const Priority& p1, const Priority& p2);
friend bool operator>(const Priority& p1, const Priority& p2);
friend bool operator<=(const Priority& p1, const Priority& p2);
friend bool operator>=(const Priority& p1, const Priority& p2);
int getLevel() const { return m_level; }
};
bool operator==(const Priority& p1, const Priority& p2)
{
return p1.m_level == p2.m_level;
}
bool operator!=(const Priority& p1, const Priority& p2)
{
return p1.m_level != p2.m_level;
}
bool operator<(const Priority& p1, const Priority& p2)
{
return p1.m_level < p2.m_level;
}
bool operator>(const Priority& p1, const Priority& p2)
{
return p1.m_level > p2.m_level;
}
bool operator<=(const Priority& p1, const Priority& p2)
{
return p1.m_level <= p2.m_level;
}
bool operator>=(const Priority& p1, const Priority& p2)
{
return p1.m_level >= p2.m_level;
}
int main()
{
Priority low{1};
Priority high{10};
if (low < high)
std::cout << "Low priority tasks come after high priority\n";
if (high >= low)
std::cout << "High priority takes precedence\n";
return 0;
}
Reducing code duplication
Notice how similar many of these operators are? We can reduce duplication by implementing most operators in terms of others:
#include <iostream>
class Priority
{
private:
int m_level{};
public:
explicit Priority(int level) : m_level{level} {}
friend bool operator==(const Priority& p1, const Priority& p2)
{
return p1.m_level == p2.m_level;
}
friend bool operator!=(const Priority& p1, const Priority& p2)
{
return !(operator==(p1, p2));
}
friend bool operator<(const Priority& p1, const Priority& p2)
{
return p1.m_level < p2.m_level;
}
friend bool operator>(const Priority& p1, const Priority& p2)
{
return operator<(p2, p1);
}
friend bool operator<=(const Priority& p1, const Priority& p2)
{
return !(operator>(p1, p2));
}
friend bool operator>=(const Priority& p1, const Priority& p2)
{
return !(operator<(p1, p2));
}
int getLevel() const { return m_level; }
};
int main()
{
Priority urgent{5};
Priority normal{3};
if (urgent > normal)
std::cout << "Urgent tasks first\n";
if (normal != urgent)
std::cout << "Different priority levels\n";
return 0;
}
Now we only need to implement the logic for operator== and operator<, and all the other operators are defined in terms of those two. This makes maintenance much easier!
The spaceship operator <=> (C++20)
C++20 introduces the spaceship operator (operator<=>), which can automatically generate all six comparison operators from a single definition. This feature greatly simplifies comparison operator implementation.
Summary
Comparison operators: Allow comparing custom class objects using familiar operators like ==, !=, <, >, <=, >=.
Implementation as friend functions: Comparison operators don't modify operands, so they're typically implemented as friend functions for symmetric access to both operands.
When comparison doesn't make sense: Not every class should have comparison operators. Only define them when there's an obvious, unambiguous way to compare objects of that type.
Implementing operators in terms of others: Reduce code duplication by implementing most comparison operators using just operator== and operator<. For example, operator!= can be !(operator==), operator> can be operator<(p2, p1), etc.
Inline friend definitions: For simple implementations like comparisons defined in terms of other operators, defining friend functions directly inside the class keeps related code together.
C++20 spaceship operator: The operator<=> can automatically generate all six comparison operators from a single definition, greatly simplifying implementation.
Comparison operators make custom classes work naturally with sorting algorithms, containers, and conditional logic, but should only be defined when comparison has clear, intuitive meaning for the class.
Comparison Operator Overloading - Quiz
Test your understanding of the lesson.
Practice Exercises
Overloading Comparison Operators
Implement the six comparison operators for a custom class. Learn to define related operators in terms of each other for consistency and reduced code duplication.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!