Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I just read one of Joel's articles in which he says:

In general, I have to admit that I’m a little bit scared of language features that hide things. When you see the code

i = j * 5;

… in C you know, at least, that j is being multiplied by five and the results stored in i.

But if you see that same snippet of code in C++, you don’t know anything. Nothing. The only way to know what’s really happening in C++ is to find out what types i and j are, something which might be declared somewhere altogether else. That’s because j might be of a type that has operator* overloaded and it does something terribly witty when you try to multiply it.

(Emphasis mine.) Scared of language features that hide things? How can you be scared of that? Isn't hiding things (also known as abstraction) one of the key ideas of object-oriented programming? Everytime you call a method a.foo(b), you don't have any idea what that might do. You have to find out what types a and b are, something which might be declared somewhere altogether else. So should we do away with object-oriented programming, because it hides too much things from the programmer?

And how is j * 5 any different from j.multiply(5), which you might have to write in a language that does not support operator overloading? Again, you would have to find out the type of j and peek inside the multiply method, because lo and behold, j might be of a type that has a multiply method that does something terribly witty.

"Muahaha, I'm an evil programmer that names a method multiply, but what it actually does is totally obscure and non-intuitive and has absolutely nothing to do whatsoever with multiplying things." Is that a scenario we must take into consideration when designing a programming language? Then we have to abandon identifiers from programming languages on the grounds that they might be misleading!

If you want to know what a method does, you can either glance at the documentation or peek inside the implementation. Operator overloading is just syntactic sugar, and I don't see how it changes the game at all.

Please enlighten me.

share|improve this question
15  
+1: Well written, well argued, interesting topic and highly debatable. A shining example of a p.se question. –  Allon Guralnek Dec 10 '10 at 12:37
17  
+1: People listen to Joel Spolsky because he writes well and is well-known. But that doesnt make him right 100% of the time. I agree with your argument. If we all followed Joel's logic here, we'd never get anywhere. –  rmx Dec 10 '10 at 12:59
5  
I'd argue that either i and j are declared locally so you can see their type quickly, or they're sucky variable names and should be renamed appropriately. –  Cameron MacFarland Dec 10 '10 at 13:40
4  
+1, but don't forget the best part of Joel's article: after running a marathon toward the correct answer, he for no apparent reason stops 50 feet short of it. Wrong code shouldn't just look wrong; it shouldn't compile. –  Larry Coleman Dec 10 '10 at 15:57
2  
@Larry: You can make wrong code fail to compile by defining classes appropriately, so in his example you could have SafeString and UnsafeString in C++, or RowIndex and ColumnIndex, but you'd then have to use operator overloading to make them behave intuitively. –  David Thornley Dec 10 '10 at 16:26

16 Answers 16

up vote 25 down vote accepted

Abstraction 'hides' code so you don't have to be concerned about the inner workings and often so you can't change them, but the intention was not to prevent you from looking at it. We just make assumptions about operators and like Joel said, it could be anywhere. Having a programming feature requiring all overloaded operators to be established in a specific location may help to find it, but I'm not sure it makes using it any easier.

I don't see making * do something that doesn't closely resemble multiplication any better than a function called Get_Some_Data that deletes data.

share|improve this answer
11  
+1 For the 'I don't see' bit. Language features are there for use, not abuse. –  Michael K Dec 10 '10 at 13:37
3  
Yet we have a << operator defined on streams which has nothing to do with the bitwise shift, right in the standard library of C++. –  Malcolm Jul 25 '13 at 16:32

I perfectly understand you do not like Joel's argument about hiding. Me neither. It's indeed much better to use '+' for things like built-in numerical types or for your own ones like, say, matrix. I admit this is neat and elegant to be able to multiply two matrices with the '*' instead of '.multiply( )'. And after all we've got the same kind of abstraction in both cases.

What hurts here is the readability of your code. In a real-life cases, not in the academic example of matrix multiplication. Especially if your language allows to define operators that are not initially present in the language core, for instance =:=. A lot of extra questions arise at this point. What is that damn operator about? I mean what is the precedence of that thing? What is the associativity? In which order is the a =:= b =:= c really executed?

That is already an argument against operator overloading. Still not convinced? Checking the precedence rules took you no more then 10 sec? Ok, let's go further.

If you start to use a language that allows operator overloading, for instance that popular one whose name begins with 'S', you will quickly learn that library designers love to override operators. Of course they are well educated, they follow the best practices (no cynicism here) and all their APIs make perfect sense when we look at them separately.

Now imagine you have to use a few APIs that make heavy use of operators overloading together in a one piece of code. Or even better - you have to read some legacy code like that. This is when the operator overloading really sucks. Basically if there is a lot of overloaded operators in one place they will soon start to mingle with the other non alpha-numerical characters in your program code. They will mingle with non alpha-numerical characters that are not really operators but rather some more fundamental language grammar elements that define things like blocks and scopes, shape flow control statements or denote some meta thingies. You will need to put the glasses and move your eyes 10 cm closer to the LCD display to understand that visual mess.

share|improve this answer
1  
Overloading existing operators and inventing new operators are not the same thing, but +1 from me. –  fredoverflow Apr 16 at 17:32

In comparison to spelled out methods, operators are shorter, but also they don't require parentheses. Parentheses are relatively inconvenient to type. And you must balance them. In total, any method call requires three characters of plain noise compared to an operator. This makes using operators very, very tempting.
Why else would anyone want to this: cout << "Hello world"?

The problem with overloading is, that most programmers are unbelievably lazy and most programmers cannot afford to be.

What drives C++ programmers to the abuse of operator overloading is the not its presence, but the absence of a neater way to perform method calls. And people are not just afraid of operator overloading because it's possible, but because it's done.
Note that for example in Ruby and Scala nobody is afraid of operator overloading. Apart from the fact, that the use of operators is not really shorter than methods, another reason is, that Ruby limits operator overloading to a sensible minimum, while Scala allows you to declare your own operators thus making avoiding collision trivial.

share|improve this answer

The reason Operator Overloading is scary, is because there are a large number of programmers that would never even THINK that * doesn't mean simply "multiply", whereas a method like foo.multiply(bar) at least instantly points out to that programmer that someone wrote a custom multiply method. At which point they would wonder why and go investigating.

I have worked with "good programmers" who were in high level positions that would create methods called "CompareValues" that would take 2 arguments, and apply the values from one to the other and return a boolean. Or a method called "LoadTheValues" that would go to the database for 3 other objects, get values, do calculations, modify this and save it to the database.

If I am working on a team with those types of programmers, I instantly know to investigate things they have worked on. If they overloaded an operator, I have no way at all of knowing that they did it except to assume they did and go looking.

In a perfect world, or a team with perfect programmers, operator overloading is probably a fantastic tool. I have yet to work on a team of perfect programmers though, so that's why it's scary.

share|improve this answer

In addition to what has already been said here, there's one more argument against operator overloading. Indeed, if you write +, this is kind of obvious that you mean addition of something to something. But this is not always the case.

C++ itself provides a great example of such a case. How is stream << 1 supposed to be read? stream shifted left by 1? It is not obvious at all unless you explicitly know that << in C++ also writes to the stream. However, if this operation were implemented as a method, no sane developer would write o.leftShift(1), it would be something like o.write(1).

The bottom line is that by making operator overloading unavailable, the language makes programmers think about the names of operations. Even if the chosen name is not perfect, it is still harder to misinterpret a name than a sign.

share|improve this answer

In general, I avoid using operator overloading in non-intuitive ways. That is, if I have a numeric class, overloading * is acceptable (and encouraged). However, if I have a class Employee, what would overloading * do? In other words, overload operators in intuitive ways that make it easy to read and understand.

Acceptable/Encouraged:

class Complex
{
public:
    double r;
    double i;

    Complex operator*(const Compex& rhs)
    {
        Complex result;
        result.r = (r * rhs.r) - (i * rhs.i);
        result.i = (r * rhs.i) + (i * rhs.r);
        return result;
    }
};

Not acceptable:

class Employee
{
public:
    std::string name;
    std::string address;
    std::string phone_number;

    Employee operator* (const Employee& e)
    {
        // what the hell do I do here??
    }
};
share|improve this answer
1  
Multiplying employees? Surely that's a sackable offence, if they do it on the boardroom table, that is. –  gbjbaanb Oct 8 '11 at 23:48

IMHO, language features such as operator overloading give the programmer more power. And, as we all know, with great power comes great responsibility. Features that give you more power also give you more ways to shoot yourself in the foot, and, obviously, should be used judiciously.

For example, it makes perfect sense to overload the + or the * operator for class Matrix or class Complex. Everyone will instantly know what it means. On the other hand, to me the fact that + means concatenation of strings is not at all obvious, even though Java does this as a part of the language, and STL does for std::string using operator overloading.

Another good example of when operator overloading makes code more clear is smart pointers in C++. You want the smart pointers to behave like regular pointers as much as possible, so it makes perfect sense to overload the unary * and -> operators.

In essence, operator overloading is nothing more than just another way to name a function. And there is a rule for naming functions: the name must be descriptive, making it immediately obvious what the function does. The same exact rule applies to operator overloading.

share|improve this answer
1  
Your last two sentences get to the heart of the objection to operator overloading: the desire for all code to be immediately obvious. –  Larry Coleman Dec 10 '10 at 16:00
2  
Isn't it obvious what M * N means, where M and N are of type Matrix? –  Dima Dec 10 '10 at 16:14
1  
@Fred: Nope. There is one kind of matrix multiplication. You can multiply an m x n matrix by an n x k matrix and get an m x k matrix. –  Dima Dec 10 '10 at 18:35
1  
@FredOverflow: There are different ways to multiply a three-dimensional vector, one giving you a scalar and one giving you another three-dimensional vector, and so overloading * for those can cause confusion. Arguably you could use operator*() for the dot product and operator%() for the cross product, but I wouldn't do that for a general-use library. –  David Thornley Dec 10 '10 at 18:48
1  
@Martin Beckett: No. C++ isn't allowed to reorder A-B as B-A either, and all operators follow that pattern. Although there is always one exception: when the compiler can prove it doesn't matter , it's allowed to rearrange everything. –  Sjoerd Apr 10 '11 at 7:13

Based on the other answers I've seen, I can only conclude that the real objection to operator overloading is the desire for immediately obvious code.

This is tragic for two reasons:

  1. Carried to its logical conclusion, the principle that code should be immediately obvious would have us all still coding in COBOL.
  2. You don't learn from code that is immediately obvious. You learn from code that makes sense once you take some time to think about how it works.
share|improve this answer

I see two problems with operator overloading.

  1. Overloading changes the semantics of the operator, even if that is not intended by the programmer. For example, when you overload &&, || or ,, you lose the sequence points that are implied by the built-in variants of these operators (as well as the short-circuiting behaviour of the logical operators). For this reason, it is better not to overload these operators, even if the language allows it.
  2. Some people see operator overloading as such a nice feature, they start to use it everywhere, even if it is not the appropriate solution. This causes other people to over-react in the other direction and warn against the use of operator overloading. I don't agree with either group, but take the middle ground: Operator overloading should be used sparingly and only when
    • the overloaded operator has the natural meaning for both the domain experts and the software experts. If those two groups do not agree on the natural meaning for the operator, don't overload it.
    • for the type(s) involved, there is no natural meaning for the operator and the immediate context (preferably same expression, but no more than a few lines) always makes it clear what the meaning is of the operator. An example of this category would be operator<< for streams.
share|improve this answer
1  
+1 from me, but the second argument can equally well be applied to inheritance. Many people don't have a clue about inheritance and try to apply it to everything. I think most programmers would agree that it is possible to misuse inheritance. Does that mean inheritance is "evil" and should be abandoned from programming languages? Or should we leave it in because it can also be useful? –  fredoverflow Dec 10 '10 at 17:42

One difference between overloading a * b and calling multiply(a,b) is that the latter can easily be grepped for. If the multiply function isn't overloaded for different types then you can find out exactly what the function is going to do, without having to track through the types of a and b.

Linus Torvalds has an interesting argument about operator overloading. In something like linux kernel development, where most of the changes are sent via patches over email, it's important that the maintainers can understand what a patch will do with only a few lines of context around each change. If functions and operators are not overloaded then the patch can more easily be read in a context independent way, as you don't have to go through the changed file working out what all of the types are and check for overloaded operators.

share|improve this answer
2  
@Scott: There is no point in discussing the "evilness" of overloading with respect to projects programmed in C, because C does not have the ability to overload functions. –  fredoverflow Dec 10 '10 at 17:47
3  
Linus Torvalds seems to me to have a narrow viewpoint. He sometimes criticizes things that aren't really useful for Linux kernel programming as if that makes them unsuitable for general use. Subversion is one example. It's a nice VCS, but Linux kernel development really needs a distributed VCS, so Linus criticized SVN in general. –  David Thornley Dec 10 '10 at 18:42

In Haskell "+", "-", "*", "/" etc are just (infix) functions.

Should you name an infix function "plus" as in "4 plus 2"? Why not, if addition is what your function does. Should you name your "plus" function "+"? Why not.

I think the issue with so called "operators" are, that they mostly resemble mathematical operations and there are not many ways to interpret those and thus there are high expectations about what such a method/function/operator does.

EDIT: made my point more clear

share|improve this answer
1  
@sbi Haskell has no distinction between builtin and user-defined. All operators are equal. You can even turn some extensions on that remove all predefined stuff and let you write anything from scratch, including any operators. –  FUZxxl Oct 8 '11 at 14:18

I think that overloading math operators is not the real issue with operator overloading in C++. I think overloading operators that should not rely on the context of the expression (i.e. type) is "evil". E.g. overloading , [ ] ( ) -> ->* new delete or even the unary *. You have a certain set of expectations from those operators that should never change.

share|improve this answer
3  
Are you saying we shouldn't be able to overload the operators you mentioned at all ? Or are you just saying that we should overload them for sane purposes only? Because I would hate to see containers without operator[], functors without operator(), smart pointers without operator-> and so on. –  fredoverflow Dec 10 '10 at 17:39

I think that operator overloads invite for abuse. Using operators instead of methods is so convenient, but there are only a few of operators available for overloading, so programmers might be tempted to use them for an operation that has no relation at all to the conventional meaning of that operator.

Of course, you might argue, a stupid programmer might use "add" as name for a function that does anything but adding. De facto, there is absolutely no motivation to do that, so nobody will. On the other hand, a programmer might overload "+" with an operation that doesn't add anything, just because he wants to have an operator, but no operator has a appropriate meaning, so the programmer simply picks +, and suddenly you have pluses everywhere and each one means something completely different.

share|improve this answer
12  
Abusing language features is really a good reason to remove language features? Wouldn't be better to educate/force programmers to follow good practices? –  Lorenzo Dec 10 '10 at 12:28
2  
@ammoQ: Which shows a lot in the Java code I've seen... –  sbi Dec 10 '10 at 12:36
5  
sbi: When a language is said to be fool-proof, chances are that it will be used by fools –  user281377 Dec 10 '10 at 12:42
2  
Hm... I guess I prefer languages that trust me, then :) –  fredoverflow Dec 10 '10 at 13:00

Based on my personal experience, the Java way of allowing multiple methods, but not overloading operator, means that whenever you see an operator you know exactly what it does.

You do not have to see if * invokes strange code but know that it is a multiply, and it behaves exactly like in the way defined by the Java Language Specification. This means you can concentrate on the actual behaviour instead of finding out all the wicket stuff defined by the programmer.

In other words, prohibiting operator overload is a benefit to the reader, not the writer, and hence makes programs easier to maintain!

share|improve this answer
4  
@Michael: About linked lists, std::list does not overload operator[] (or give any other means of indexing into the list), because such an operation would be O(n), and a list interface should not expose such a function if you care about efficiency. Clients might be tempted to iterate over linked lists with indexes, making O(n) algorithms needlessly O(n^2). You see that quite often in Java code, especially if people work with the List interface which aims to abstract complexity away completely. –  fredoverflow Dec 10 '10 at 17:23
4  
@Thor: "But in order to be certain you have to check :)"... Again, this is not tied to operator overloading. If you see time.add(anotherTime), you will also have to check if the library programmer implemented the add operation "correctly" (whatever that means). –  fredoverflow Dec 10 '10 at 17:35

I somewhat agree.

If you write multiply(j,5), j could be of a scalar or matrix type, making multiply() more or less complex, depending on what j is. However, if you abandon the idea of overloading altogether, then the function would have to be named multiply_scalar() or multiply_matrix() which would make it obvious what's happening underneath.

There's code where many of us would prefer it one way and there's code where most of us would prefer it the other way. Most of the code, however, falls into thew middle ground between those two extremes. What you prefer there depends on your background and personal preferences.

share|improve this answer
2  
You're rather optimistic about names aren't you? Based on some places I've worked, I'd expect names like 'multiply()` and 'multiplym()` or maybe real_multiply() or so. Developers often aren't good with names, and operator*() at least is going to be consistent. –  David Thornley Dec 10 '10 at 18:56
4  
@David: And since naming things is hard, names should be banished from programming languages, right? It's just too easy to get them wrong! ;-) –  fredoverflow Dec 11 '10 at 8:04

I suspect it has something to do with breaking expectations. I've you're used to C++, you're used to operator behavior not being dictated entirely by the language, and you won't be surprised when an operator does something odd. If you're used to languages that don't have that feature, and then see C++ code, you bring along the expectations from those other languages, and may be nastily surprised when you discover that an overloaded operator does something funky.

Personally I think there's a difference. When you can change the behavior of the language's built-in syntax, it becomes more opaque to reason about. Languages that don't allow meta-programming are syntactically less powerful, but conceptually simpler to understand.

share|improve this answer

protected by Telastyn Apr 16 at 17:49

Thank you for your interest in this question. Because it has attracted low-quality answers, posting an answer now requires 10 reputation on this site.

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.