I'm tempted to say that there isn't anything to learn about operator
overloading; overloaded operators are just funny named functions. What
you do have to learn is when overloading is appropriate, and when it is
not. Certain idioms are more or less standard: numeric types overload
the appropriate numeric operators (+
, etc.), smart pointers overload
the pointer ops (*
and ->
), container types which support indexation
overload []
, and functional objects overload ()
. And that's about
it for the special cases. And while it's arguably an abuse, if you
define an iterator, you'll want it to support the standard iterator
idiom (which means ++
, *
, ->
and ==
/!=
).
In addition to these, there are three operators which will be overloaded
for many different types of classes: assignment is done using =
, and
insertion into and extraction from streams is done using <<
and >>
.
If you want your class to support any of these, you should use the
overload. And comparison uses ==
and !=
for equality, and <
,
<=
, >
and >=
for inequality. But don't overload for inequality
unless it is semantically significant. It's better to provide an
explicit ordering function for use with std::map
and std::set
than
to mislead readers into thinking you've defined as semantically
significant ordering. You might want to specialize std::less
on your
class in this case; <
won't work, or will have inappropriate semantics
for use as a key, but std::less
will define an arbitrary ordering
which will. And while it's not operator overloading, if the type is to
be used as a key in associative containers, you'll also want to provide
a function hash_code
and an instantiation of struct std::hash
.
In many cases, it's best to define the overload in terms of some more
global function: for example: I use compare
(returning an int
less
than, equal to, or greater than 0), for inequality; I'll define a public
function compare
in the class, and then derive from something like:
template<typename T>
struct ComparisonOperators
{
friend bool operator==( T const& lhs, T const& rhs )
{
return lhs.compare() < 0;
}
// ...
// The same for the other five operators.
};
(My implementation is actually somewhat more complex, as it uses
metaprogramming to use isEqual
for ==
and !=
if the class provides
it.)
I use a similar techique for the binary arithmetic operators, defining
+
in terms of +=
, etc. I also use it for IO, defining <<
in terms
of print
and >>
in terms of parse
; this is mainly useful when the
operators need to be polymorphic.