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
13  
+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
14  
+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
3  
+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
show 3 more comments

15 Answers 15

up vote 21 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
10  
+1 For the 'I don't see' bit. Language features are there for use, not abuse. –  Michael K Dec 10 '10 at 13:37
1  
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
    
the 'bitwise shift' operator is only called that for historical reasons. When applied to standard types, it does a bitwise shift (in the same way that the + operator adds numbers together when applied to numeric types), however when applied to a complex type, it can do what it likes, as long as it makes sense for that type. –  gbjbaanb Apr 7 at 9:33
add comment

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
1  
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
show 4 more comments

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
    
Erm, except for what's inherited from C, C++ (and that's what Fred was asking about) does pretty much the same thing. Now what's your take on whether this is good or bad? –  sbi Dec 10 '10 at 12:39
    
@sbi I love operator overloading... Actually even C has overloaded operators... You can use them for int, float, long long and whatever. So what's that all about? –  FUZxxl Oct 8 '11 at 14:04
    
@FUZxxl: This is all about user-defined operators overloading the built-in ones. –  sbi Oct 8 '11 at 14:12
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
    
@FUZxxl: That might well be, but those opposing overloaded operators usually do not oppose using built-in + for different built-in number types, but creating user-defined overloads. hence my comment. –  sbi Oct 8 '11 at 14:23
show 1 more comment

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
    
Good point. However, abandoning overloading altogether does not play nice with generic programming... –  FredOverflow Dec 10 '10 at 12:20
    
@FredO: Of course not. But generic programming is all about using the same algorithm for very different types, so those preferring multiply_matrix() won't like generic programming either. –  sbi Dec 10 '10 at 13:04
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
    
@David: Yeah, I skipped over the fact that the names might be bad. But then we might just as well assume that operator*() might do something stupid, j is a macro evaluating to an expressions involving five function calls, and whatnot. You then can't compare the two approaches anymore then. But, yes, naming things well is hard, although well worth whatever time it takes. –  sbi Dec 10 '10 at 19:21
3  
@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
show 4 more comments

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
add comment

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
    
@FredOverflow The second argument can be applied to anything that is "new and hot". I am not giving it as an argument to remove operator overloading from a language, but as a reason why people argue against it. As far as I am concerned, operator overloading is useful but should be used with care. –  Bart van Ingen Schenau Dec 14 '10 at 10:50
    
IMHO, allowing overloads of && and || in a way which does not imply sequencing was a big mistake (IMHO, if C++ was going to allow overloading of those, it should have used a special "two-function" format, with the first function being required to return a type which was implicitly convertible to an integer; the second function could take two or three arguments, with the "extra" argument of the second function being the return type of the first. The compiler would call the first function and then, if it returned non-zero, evaluate the second operand and call the second function upon it.) –  supercat Jul 19 '12 at 15:54
    
Of course, that's not nearly as bizarre as allowing the comma operator to be overloaded. Incidentally, one overloading-ish thing I've not really seen, but would like to, would be a means of tight-binding member access, allowing an expression like foo.bar[3].X to be handled by foo's class, rather than requiring foo to expose a member which could support subscripting and then expose a member X. If one wanted to force evaluation via actual member access, one would write ((foo.bar)[3]).X. –  supercat Jul 19 '12 at 16:02
add comment

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
    
+1, with a caveat: C++ gives you enough rope to hang yourself. But if I want to implement a linked list in C++, I'd want the ability to use [] to access the nth element. It makes sense to use the operators for data that (mathematically speaking) they are valid for. –  Michael K Dec 10 '10 at 13:41
    
@Michael, you cannot live with the list.get(n) syntax? –  user1249 Dec 10 '10 at 13:44
    
@Thorbjørn: It's actually fine as well, perhaps a poor example. Time may be better - overloading +,- would make sense rather than time.add(anotherTime). –  Michael K Dec 10 '10 at 13:50
3  
@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
3  
@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
show 8 more comments

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
    
+1 Don't make [] the equivalent of ++. –  Michael K Dec 10 '10 at 13:38
2  
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'm saying that the potential problem of operator overloading with math operations is small compared to those operators. Doing something clever or crazy with math operators might troublesome, but the operators I listed, which people usually don't think of as operators but rather basic language elements, should always meet the expectation defined by the language. [] should always be an array-like accessor, and -> should always mean accessing a member. It doesn't matter if it's actually an array or a different container, or if it's a smart pointer or not. –  Allon Guralnek Dec 10 '10 at 21:44
add comment

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
    
Isn't the linux kernel developed in pure C? Why discuss (operator) overloading at all in this context? –  FredOverflow Dec 10 '10 at 13:26
    
The concerns are the same for any project with a similar development process, regardless of language. Excessive overloading can make it difficult to understand the impact of changes if all you have to go on are a few lines from a patch file. –  Scott Wales Dec 10 '10 at 13:34
    
@FredOverflow: The Linux kernel is in GCC C really. It uses all sorts of extensions that give its C an almost C++ feel at some times. I'm thinking of some of the fancy type manipulations. –  Zan Lynx Dec 10 '10 at 17:39
1  
@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
show 4 more comments

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
    
Overloaded operators should never do "something odd". It's fine if it does something complex, of course. But only overload when it has one, obvious meaning. –  Sjoerd Apr 10 '11 at 7:18
add comment

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
    
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
add comment

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
add comment

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
    
or, in C#, for using += to tie an event to a delegate. I don't think that blaming language features for programmer stupidity is a constructive way forward. –  gbjbaanb Oct 8 '11 at 23:50
add comment

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
add comment

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
    
Lorenzo: That's surely a valid question, and IMO there is no absolute answer. Some language designers (e.g. Java) obviously don't want to depend on programmer's education. –  user281377 Dec 10 '10 at 12:33
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
show 2 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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