86

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.

fredoverflow
  • 6,874
  • 21
    +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
  • 19
    +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. – Nobody 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
  • 5
    +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
  • 3
    @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
  • @David: That's exactly my point: Joel could have suggested doing just what you describe in his article. – Larry Coleman Dec 10 '10 at 16:31
  • Joel's article becomes less important if you can assume that there is a very good debugger which can tell you the EXACT overloaded operator that is being used. Also, if you code in .Net, you can use an IDisposable interface together with a using statement (if that applies). – Job Dec 10 '10 at 22:46
  • This is as much a problem with C++ as with overloading per se. C++ has silent type conversions and a legacy of "really smart" hacks that turned out not to be such a good idea. – Móż Jan 31 '14 at 02:52
  • On top of a language like C where a builtin language construct translates pretty much to at most a very small handful of instructions, I see the point in not allowing operator overloading. The possibility of allowing function calls to hide under operators destroys that nice predictability property of C. I can't think of any good arguments against function overloading though (and I'd really appreciate some because otherwise it's going to get included in the language I'm making :D). – Petr Skocik Jul 05 '18 at 21:36

15 Answers15

34

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.

JeffO
  • 36,816
  • 14
    +1 For the 'I don't see' bit. Language features are there for use, not abuse. – Michael K Dec 10 '10 at 13:37
  • 5
    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 07 '14 at 09:33
  • 1
  • is also used for dereferencing (as done by smart pointers and iterators); it's not clear where to put the boundary between good and bad overloading
  • – martinkunev Feb 09 '15 at 12:57
  • It wouldn't be just anywhere, it'd be in the type definition of j. – Andy Oct 29 '15 at 01:07
  • @Malcolm and yet there's no ambiguity in that because streams obviously can't be left-shifted. (I also saw a matrix library that allowed m << 1,2,3, 4,5,6, 7,8,9; to fill a matrix, and matrices can't be left-shifted either, and if they could, it would make no sense to do that on the left side of a comma operator) – user253751 Jan 23 '18 at 04:36
  • @immibis There is no ambiguity. There is unintuitiveness. – Malcolm Jan 23 '18 at 13:39
  • @Andy - won't most devs go looking at a lot of other places to fix a particular strange occurrence/bug before checking type definitions for potentially over-loaded operators? If there are strong cases when this is the best solution, then it will be easy to find because you'll know were to look. – JeffO Jan 23 '18 at 15:23
  • @Malcolm I'd say that it's also unintuitive for << to mean left shift. Surely it should be the much-less-than operator? – user253751 Jan 23 '18 at 21:36
  • @JeffO Same as they would look for a potentially wrongly overridden .equals – user253751 Jan 23 '18 at 21:41
  • If you're debugging, and everything is fine until i = j * k executes, and immediately after that code is executed things are not fine, it seems like there's not a lot of places left to look. – Andy Jan 24 '18 at 01:27
  • @immibis What is unintuitive about << itself? It is an operator defined by the language, you either know what it means or not. It had existed at least since BCPL and had a well-established meaning by the time C++ appeared. And then it suddenly starts to mean some completely unrelated operation, also depending on what it is called on - that's what is unintuitive. – Malcolm Jan 24 '18 at 09:46
  • @Malcolm << means "is much less than" the same way < means "is less than". The "left shift" meaning is specific to C-derived languages. That's the unintuitiveness. There is no difference between the reasoning of "<< normally means 'is much less than', but in C++, it means 'left shift', so that's unintuitive" and "<< normally means 'left shift', but with streams it means 'output', so that's unintuitive". The only reason you find << as output unintuitive is that you didn't grow up with streams but you did grow up with left shifts. – user253751 Jan 24 '18 at 21:54
  • @immibis By your logic nothing can be unintuitive because if it is, you just "didn't grow up with this". That's clearly false. To address the specific examples, means "much less than". And in mathematics. << in programming didn't mean anything until it became a language operator. What is more related, C syntax and mathematical notation or C syntax and C++ syntax? And if it's OK to start using << for stream output, then would it be fine to use = for range creation or something? – Malcolm Jan 25 '18 at 09:39
  • By the unintuitiveness argument above, this->m_var is unintuitive because -> means "minus greater than". Even though that makes absolutely no sense. (Nor does "much less than", in programming, because without specifying a range/magnitude how do you define "much"?) – FeRD May 15 '21 at 09:40