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.