Define 'sense', please! Seriously, the bitter and impassioned debates over precisely this point show that there is huge disagreement about exactly this. Many authorities reject overloaded operators because they can be made to do utterly unexpected things. Others reply that method names can likewise be chosen to be completely unintuitive, but that is no reason for rejecting named code blocks! You are almost certainly not going to get any examples that are generally considered sensible. Examples that seem sensible to you - maybe.
–
Kilian FothFeb 23 '12 at 12:59

Completely agree with @KilianFoth. Ultimately the program that compiles, does make sense to compiler. But if overload == to do multiplication, it make sense to me but may not make sense to others! Is this question about legitimacy of what facility programming languages or are we talking about 'coding best practices'?
–
Dipan MehtaFeb 24 '12 at 13:33

11 Answers
11

The obvious examples of appropriate operator overloading are any classes which behave in the same way that numbers operate. So BigInt classes (as Jalayn suggests), complex numbers or matrix classes (as Superbest suggests) all have the same operations that ordinary numbers have so map really well onto mathematical operators, while time operations (as suggested by svick) map nicely onto a subset of those operations.

Slightly more abstractly, operators could be used when performing set like operations, so operator+ could be a union, operator- could be a complement etc. This does start to stretch the paradigm though, especially if you use the addition or multiply operator for an operation which isn't commutative, as you might expect them to be.

C# itself has an excellent example of non-numeric operator overloading. It uses += and -= to add and subtract delegates, i.e. register and de-register them. This works well because the += and -= operators work as you would expect them to, and this result in much more concise code.

For the purist, one of the problems with the string + operator is that it isn't commutative. "a"+"b" is not the same as "b"+"a". We understand this exception for strings because it is so common, but how can we tell if using operator+ on other types will be commutative or not? Most people will assume that it is, unless the object is string-like, but you never really know what people will assume.

As with strings, the foibles of matrices are pretty well known too. It is obvious that Matrix operator* (double, Matrix) is a scalar multiplication, whereas Matrix operator* (Matrix, Matrix) would be a matrix multiplication (i.e. a matrix of dot-product multiplications) for instance.

Similarly the use of operators with delegates is so obviously far removed from maths that you are unlikely to make those mistakes.

Operator overloading is a very powerful semantic technique, but it is easy to over-use. Ideally you should only use it in situations when it is very clear from context what the effect of an overloaded operator is. In many ways a.union(b) is clearer than a+b, and a*b is much more obscure than a.cartesianProduct(b), especially since the result of a cartesian product would be a SetLike<Tuple<T,T>> rather than a SetLike<T>.

The real problems with operator overloading come when a programmer assumes a class will behave in one way, but it actually behaves in another. This sort of semantic clash is what I'm suggesting it is important to try to avoid.

You say that operators on matrices map really well, but matrix multiplication isn't commutative either. Also operators on delegates are even stronger. You can do d1 + d2 for any two delegates of the same type.
–
svickFeb 23 '12 at 18:29

1

@Mark: The "dot product" is only defined on vectors; multiplying two matrices is called simply "matrix multiplication." The distinction is more than just semantic: the dot product returns a scalar, while matrix-multiplication returns a matrix (and is, by the way, non-commutative).
–
BlueRaja - Danny PflughoeftFeb 23 '12 at 20:21

The first example that comes to my mind is the implementation of BigInteger, which allows you to work with large signed integers.
Check out the MSDN link to see how many operators have been overloaded (that is, there is a big list, and I did not check if all operators have been overloaded, but it certainly seems so)

Also, since I also do Java and Java does not allow overloading operators, it's incredibly sweeter to write

I'm glad I saw this because I've been fooling around with Irony and it has a GREAT use of operator overloading. Here is a sample of what it can do.

So Irony is a ".NET Language Implementation Kit" and is a parser generator (generating an LALR parser). Instead of having to learn a new syntax/language like parser generators such as yacc/lex you write the grammar in C# with the operator overload. Here is a simple BNF grammar

If you want to easily compare two objects with by data values instead of by reference, you'll want to overload .Equals (and.GetHashCode!), and might want to do the != and == operators as well for consistency.

I've never seen any wild overloads of other operators in C# though (I imagine there are edge cases where it might be useful though).

overloading operator== and operator!= show two schools of thought: those for saying it makes things easier, and those against saying it prevents comparing addresses (i.e. am I pointing to the exact same place in memory, not just a copy of the same object).

I find cast operator overloads to be handy in specific situations. For example, I had to serialize/deserialize in XML a boolean represented as 0 or 1. The right (implicit or explicit, I forget) cast operator from boolean to int and back did the trick.

Another way to compare addresses is to force the use of object's == by casting: (object)foo == (object)bar always compares references. But I would prefer ReferenceEquals(), as @dan04 mentions because it's clearer what it does.
–
svickFeb 24 '12 at 12:33

They're not in the category of things that people typically think of when they thing of operator overloading, but I think one of the most important operators to be able to overload is the conversion operator.

Conversion operators are especially useful for value types that can "de-sugar" to a numeric type, or can act like a numeric type in some contexts. For example, you might define a special Id type that represents a certain identifier, and you could provide an implicit conversion to int so that you can pass an Id to a method that takes an int, but an explict conversion from int to Id so no one can pass an int into a method that takes an Id without casting it first.

As an example outside of C#, the Python language includes many special behaviors that are implemented as overloadable operators. These include the in operator for membership testing, the () operator for calling an object as if it's a function, and the len operator for determining the length or size of an object.

And then you have languages like Haskell, Scala, and many other functional languages, where names like + are just ordinary functions, and not operators at all (and there is language support for using functions in infix position).

You add vectors, not positions :\ This is a good example of when operator+ should not be overloaded (you can implement a point in terms of a vector, but you should not be able to add two points)
–
BlueRaja - Danny PflughoeftFeb 23 '12 at 20:28

@BlueRaja-DannyPflughoeft: Adding positions to yield another position doesn't make sense, but subtracting them (to yield a vector) does, as does averaging them. One could compute the average of p1, p2, p3, and p4 via p1+((p2-p1)+(p3-p1)+(p4-p1))/4, but that seems somewhat awkward.
–
supercatFeb 24 '14 at 23:56

In affine geometry you can do algebra with points and lines, like addition, scaling etc. The implementation though requires homogeneous coordinates, which are typically used in 3D graphics anyway. The addition of two points actually results in their average.
–
ja72Feb 25 '14 at 13:42