4 ms·
I agree with all of this, except the operator overloading rant. Unless of course we extend it to forbiding overloading of any function name for more than one ty
by CJefferson 15y ago
I agree with all of this, except the operator overloading rant. Unless of course we extend it to forbiding overloading of any function name for more than one type.
I have done a bunch of mathematical programming in java, and you end up writing ".add()" a lot. Maybe .add is on a finite field. Maybe it is some matrices. Maybe complex numbers. I don't see what I am saving over operator overloading.
Of course, if I used .addFiniteField, .addMatrix, .addComplex, then I can see the (possible) benefit, of knowing how "heavy" the operation is at a glance. However, I don't think it is worth the cost, given I lose genericness.
- robfig 15y agoIt sounds like you would use operator overloading for exactly what it was meant for (and good at)! I think the usual complaint is that people use operator overloading for non-mathematical operations. For example, a guy doing a webapp might overload the "+" operator for Group and User as a clever way to add a User to a Group. This turns out to be a terrible idea. In fact, my eyes glaze over as I read Scala code for this very reason, because everyone and their mother defines a method with some random array of symbols because it makes sense to them and they prefer shorthand. It makes the code totally unreadable.
- CJefferson 15y agoYes, that aspect of operator overloading is nasty. I think one thing that makes operator overloading worse is that there are few reasons to use 'add()' rather than 'addUser()', but once you decide to use operator overloading, there are limited options. C++ libraries which do abuse operator overloading (see the boost::spirit parser library as the best example) have this problem. For example, spirit wants to define the standard '' and '+' parsing operators. However 'a+' and 'a' aren't valid C++, while '+a' (unary +) and '*a' (dereference) are, so those are used.
- scott_s 15y agoSpirit doesn't bother me, although it's a glaring exception I did not think of to my above response. Its use of operator overloading doesn't bother me because that's the whole point of the library; you know what you're getting into ahead of time. It let me write EBNF code directly in C++: https://github.com/scotts/cellgen/blob/master/src/cellgen_grammar.cpp#L336 https://github.com/scotts/cellgen/blob/master/src/cellgen_gr... The Spirit code is unlikely to be mixed in with other code. Or, put another way, I was never bothered by your complaint, because I always read it as EBNF notation first. My real problem with Spirit was performance. It took three minutes to compile a 7000 line C++ program - more with optimizations turned on. The binary with debugging information turned on was 24 MB. And the enormous amount of copying it performed at runtime meant parsing 300 line files took a few seconds. (Luckily the runtime performance of my compiler was never important.) The new version may have fixed these problems.
- scott_s 15y agoThat's the perennial complaint and worry, but in my experience, people use operator overloading in C++ for mainly two things: function objects by overloading operator(), and iterators by overloading the dereferencing operators and increment operators.
- xyzzyz 15y agoAt any given place, people use at most 30% of C++ features -- the bad thing is that these 30% subsets usually overlap only a little. People use operator overloading in C++ for all the crazy reasons, like I/O, for instance -- for even better (or maybe worse) examples, see boost, boost::lambda will be a good starting point. Good abstractions are good, but operator overloading combined with templates and implicit casting is not one of them.
- scott_s 15y agoshrug People always talk about it what can happen, but I've never seen instances of operator overloading in C++ that I objected to in a fundamental way.
- xyzzyz 15y agoiostream, for one. It's a real pain to deal simultaneously with this fancy bit-shift I/O syntax on the one hand, and i18n on the other -- fancy syntax is way inferior to format strings when you want to support multiple languages. Also, please, take a look at the boost::lambda or other boost libraries -- they have large DSLs written on top of operator overloading which look nice when you look at them, but are terribly complicated to use and ridiculously hard to debug, thanks to the completely unintelligible messages that the compilers produce. Most probable reason you've never seen it, is that most sane, experienced people know the danger and avoid it. When there's more than one person working on a project, it's way more important for the code to be easy to follow and debug than to look fancy.
- scott_s 15y ago
- eru 15y agoOcaml and especially Haskell have more sane policies towards operators. You can make up your own operators. That's not always readable, but at least if you see some funny symbols, you know that you have to look it up. With Haskell's typeclasses (and I guess Ocaml's Functors should allow the same) you can have the functions of the same name that work differently for different types. E.g. there's a Show-typeclass, whose instances have to provide a `show :: a -> String' function. The Num-typeclass includes operators such as `(+) :: a -> a -> a', which means that + always takes to arguments of the same type and returns the same. So you wouldn't be able to overload + to add a user to a group. You might come across somebody writing (+!) :: User -> Group -> IO () for adding a user to a group (in an un-idiomatic way). But at least there's a funny symbol to alert you, instead of a re-used normal one.