Summary of Canonical Function Signatures
Many operator overloads can return pretty much anything. For example, nothing stops you from returning void
in operator==
.However, only a few of these signatures are canonical, which means that you would normally write them that way, and that such an operator can be explicitly defaulted with = default
.
Assignment Operators
struct X { X& operator=(const X&) = default; // copy assignment operator X& operator=(X&&) noexcept = default; // move assignment operator};
Explicit defaulting with = default;
is possible, but you can also implement assignment manually.Move assignment is almost always noexcept
, although it isn't mandatory.
Comparison Operators
#include <compare> // for comparison categoriesstruct X { friend auto operator<=>(const X&, const X&) = default; // defaulted three-way comparison friend std::strong_ordering<=>(const X&, const X&); // manual three-way comparison friend bool operator==(const X&, const X&) = default; // equality comparisons friend bool operator!=(const X&, const X&) = default; // defaultable since C++20 friend bool operator<(const X&, const X&) = default; // relational comparisons friend bool operator>(const X&, const X&) = default; // defaultable since C++20 friend bool operator<=(const X&, const X&) = default; friend bool operator>=(const X&, const X&) = default;};
See this answer for more information on when and how to default/implement comparisons.
Arithmetic Operators
struct X { friend X operator+(const X&, const X&); // binary plus friend X operator*(const X&, const X&); // binary multiplication friend X operator-(const X&, const X&); // binary minus friend X operator/(const X&, const X&); // binary division friend X operator%(const X&, const X&); // binary remainder X operator+() const; // unary plus X operator-() const; // unary minus X& operator++(); // prefix increment X& operator--(); // prefix decrement X operator++(int); // postfix increment X operator--(int); // postfix decrement X& operator+=(const X&); // compound arithmetic assignment X& operator-=(const X&); X& operator*(const X&); X& operator/=(const X&); X& operator%=(const X&);};
It is also possible to take the left operator of binary operators by value, but this is not recommended because it makes the signature asymmetric and inhibits compiler optimizations.
Bitwise Operators
struct X { using difference_type = /* some integer type */; friend X operator&(const X&, const X&); // bitwise AND friend X operator|(const X&, const X&); // bitwise OR friend X operator^(const X&, const X&); // bitwise XOR friend X operator<<(const X&, difference_type); // bitwise left-shift friend X operator>>(const X&, difference_type); // bitwise right-shift X operator~() const; // bitwise NOT X& operator&=(const X&); // compound bitwise assignment X& operator|=(const X&); X& operator^(const X&); X& operator/=(const X&); X& operator%=(const X&);};
Stream Insertion and Extraction
#include <ostream> // std::ostream#include <istream> // std::istreamstruct X { friend std::ostream& operator<<(std::ostream&, const X&); // stream insertion friend std::istream& operator>>(std::istream&, X&); // stream extraction};
Function Call Operator
struct X { using result = /* ... */; result operator()(user-defined-args...) /* const / volatile / & / && */; static result operator()(user-defined-args...); // since C++23};
Subscript Operator
struct X { using key_type = /* ... */; using value_type = /* ... */; const value_type& operator[](key_type) const; value_type& operator[](key_type); static value_type& operator[](key_type); // since C++23};
Note that operator[]
can accept multiple parameters since C++23.
Member Access Operators
struct X { using value_type = /* ... */; const value_type& operator*() const; // indirection operator value_type& operator*(); const value_type* operator->() const; // arrow operator value_type* operator->();};
Pointer-to-Member Operator
struct X { using member_type = /* ... */; using member_pointer_type = /* ... */; const member_type& operator->*(member_pointer_type) const; member_type& operator->*(member_pointer_type);};
Address-of Operator
struct X { using address_type = /* ... */; address_type operator&() const; // address-of operator};
Logical Operators
struct X { friend X operator&&(const X&, const X&); // logical AND friend X operator||(const X&, const X&); // logical OR friend X operator!(const X&); // logical NOT};
Note that these don't return bool
because they only make sense if X
is already a logical type that similar to bool
.
User-defined Conversions
struct X { using type = /* ... */; operator type() const; // arbitrary implicit conversion explicit operator bool() const; // explicit/contextual conversion to bool template <typename T> requires /* ... */ // optionally constrained explicit operator T() const; // conversion function template};
Coroutine Await
struct X { using awaiter = /* ... */; awaiter operator co_await() const;};
Comma Operator
struct X { using pair_type = /* ... */; // often a template to support combination of arbitrary types friend pair_type operator,(const X&, const X&);};
Allocation Functions
struct X { // class-specific allocation functions void* operator new(std::size_t); void* operator new[](std::size_t); void* operator new(std::size_t, std::align_val_t); // C++17 void* operator new[](std::size_t, std::align_val_t); // C++17 // class-specific placement allocation functions void* operator new(std::size_t, user-defined-args...); void* operator new[](std::size_t, user-defined-args...); void* operator new(std::size_t, std::align_val_t, user-defined-args...); // C++17 void* operator new[](std::size_t, std::align_val_t, user-defined-args...); // C++17 // class-specific usual deallocation functions void operator delete(void*); void operator delete[](void*); void operator delete(void*, std::align_val_t); // C++17 void operator delete[](void*, std::align_val_t); // C++17 void operator delete(void*, std::size_t); void operator delete[](void*, std::size_t); void operator delete(void*, std::size_t, std::align_val_t); // C++17 void operator delete[](void*, std::size_t, std::align_val_t); // C++17 // class-specific placement deallocation functions void operator delete(void*, user-defined-args...); void operator delete(void*, user-defined-args...); // class-specific usual destroying deallocation functions void operator delete(X*, std::destroying_delete_t); // C++20 void operator delete(X*, std::destroying_delete_t, std::align_val_t); // C++20 void operator delete(X*, std::destroying_delete_t, std::size_t); // C++20 void operator delete(X*, std::destroying_delete_t, std::size_t, std::align_val_t); // C++20};// non-class specific replaceable allocation functions ...void* operator new(std::size_t);void* operator delete(void*);// ...