Operator Overloading
Operator overloading is the redefinition of operators to support operations on specific data types. Operator overloading is a special case of function overloading.
When an operator appears in an expression, and at least one of its operands has a class or enumeration type, overload resolution is used to determine which user-defined function satisfying the corresponding declaration should be called.1
In simpler terms, if using an "operator" is considered as calling a special function (e.g., treating 1+2 as calling add(1, 2)), and at least one of the function's parameters (operands) is of type class, struct, or enum, the compiler needs to decide which custom function to call based on the operand types.
In C++, we can overload almost all available operators.
List of Some Overloadable Operators
Unary operators: + (positive sign); - (negative sign); ~ (bitwise NOT); ++; --; ! (logical NOT); * (dereference); & (address-of); -> (member access) etc.
Binary operators: +; -; & (bitwise AND); [] (subscript); ==; = (assignment) etc.
Others: () (function call); "" (user-defined literals1, since C++11); new (memory allocation); , (comma operator); <=> (three-way comparison2, since C++20) etc.
Restrictions
The following restrictions apply to operator overloading:
- Only existing operators can be overloaded; new operators cannot be defined.
- The following operators cannot be overloaded:
::(scope resolution),.(member access),.*(member access through pointer to member),?:(ternary conditional). - The precedence, number of operands, and associativity of an overloaded operator cannot be changed.
- Overloading
&&(logical AND) and||(logical OR) loses short-circuit evaluation.
Implementation
Operator overloading can be implemented in two ways: as a member function or as a non-member function.
When overloaded as a member function, there is an implicit this pointer pointing to the current object as a parameter, so the number of function parameters is one less than the number of operands.
When overloaded as a non-member function, the number of function parameters equals the number of operands.
The basic format is as follows (assuming the operator to be overloaded is @):
1 2 3 4 5 6 7 | |
Several examples of operator overloading are given below.
Basic Arithmetic Operators
The following defines a 2D vector struct Vector2D and overloads the addition and dot product operators.
Example of Overloading Arithmetic Operators
1 2 3 4 5 6 7 8 9 10 | |
Increment and Decrement Operators
Increment and decrement operators are divided into two categories: prefix (++a) and postfix (a++). To distinguish between them, when overloading the postfix operator, an extra dummy parameter of type int must be added.
Prefix increment can be thought of as calling operator++(a) or a.operator++(), while postfix increment can be thought of as calling operator++(a, 0) or a.operator++(0).
Example Overloading Prefix and Postfix Increment Operators Separately
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Another point is that for built-in increment/decrement operators, the prefix operator returns a reference, while the postfix operator returns a value. Although overloaded operators are not required to follow this restriction, semantically it is still expected that the return types of overloaded operators match those of the built-in operators.
For a type T, typical definitions for overloading the increment operator are as follows:
Overload Definition (using ++ as example) |
Member Function | Non-member Function |
|---|---|---|
| Prefix | T& T::operator++(); |
T& operator++(T& a); |
| Postfix | T T::operator++(int); |
T operator++(T& a, int); |
Function Call Operator
The function call operator () can only be overloaded as a member function. By overloading the () operator for a class, objects of that class can be called like functions.
A common application of overloading the () operator is to pass a struct with an overloaded () operator as a custom comparison function into STL containers like priority queues.
Here is an example: Given the names and scores of \(n\) students, sort them in descending order by score; for equal scores, sort in ascending order by name lexicographically, and output the name and score of the top-ranked student.
The following defines a comparison struct to implement a custom sorting method for a priority queue.
Example of Overloading the Function Call Operator
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Comparison Operators
In std::sort and some STL containers, the < operator is required. When using custom types, we need to overload it manually.
The following example achieves the same functionality as the previous section.
Example of Overloading Comparison Operators
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
The code above overloads the less-than operator as a member function, but overloading it as a non-member function is also possible.
Overloading as a Non-member Function
1 2 3 4 5 6 7 8 9 10 | |
In fact, once the < operator is defined, the other five comparison operators can be easily implemented.
1 2 3 4 5 6 7 8 9 10 | |
About the Three-Way Comparison Operator in C++20
If using C++20 or later, we can directly use the default three-way comparison operator to simplify the code.3
1 | |
The default comparison compares member variables in the order they are declared.4
Custom three-way comparison is also possible. In this case, you must choose the underlying ordering relation (std::strong_ordering, std::weak_ordering, or std::partial_ordering), or return an object such that:
- If
a < b, then(a <=> b) < 0; - If
a > b, then(a <=> b) > 0; - If
aandbare equal or equivalent, then(a <=> b) == 0.
For specific implementation details, refer to Comparison operators #Three-way comparison - cppreference.
References and Notes:
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用