Uri
Uri

Reputation: 89739

Are operator overloadings in C++ more trouble than they're worth?

In my experience teaching C++, operator overloading is one of those topics that causes the most grief to students. Even looking at questions here at stackoverflow: for example, make the + operator external or a member? How to handle symmetry, etc., it seems like it's a lot of trouble.

When I moved from C++ to Java, I was worried I would miss that ability, but with the exception of operators like [] or (), I haven't really ever felt the need to overload operators. In fact, I feel programs without them are more readable.

Note: I put this as a community wiki. Let's discuss this. I want to hear opinions.

Upvotes: 8

Views: 3317

Answers (14)

JaredPar
JaredPar

Reputation: 754695

I think it really depends on the use case. There are several types of classes for which operators are a necessity. For instance, smart pointers would be worthless without the -> and * operators.

I also find the comparision, equality and assignment operators to be very useful for specific types. I work in an editor environment and as such we naturally have several times for representing coordinates and spans. Sure we could do everything with a comparison operator but

if ( point1 > point2 ) ...

Just looks loads better than

if ( point1.Compare(point2) < 0 ) ...

I find less use for the other operators although cast occasionally comes in handy.

Upvotes: 4

tpdi
tpdi

Reputation: 35141

I miss having overloaded operators in Java, especially in the following situations:

A class that is an algorithm or a functor: (Strategy Pattern, Chain of Responsibility, Interpreter, etc.). The natural thing is to overload op(); instead, every programmer comes up with (often incompatible and thus confusing) names for functions: "eval", "evaluate", "operation", "doIt", etc. So clarity is reduced, because these names we're forced to use don't make their meaning obvious.

A class that has a conversion to another type: In C++ that's operator Type(), and works both for actual conversion and for yielding an internal member of the desired class. The second case comes up a lot in Java when a class is unnecessarily final but you want to add operations to it:

class DecoratedStringBuffer { //extends StringBuffer doesn't work, as String is final
  private String byContainmentThen;
  public decorate(final String prefix, final String suffix) { ... }
  public append(final String s) { byContainmentThen.append(s);}
  // other forwarding functions
}

Since DecoratedStringBuffer isn't is-a StringBuffer, before it leaves your code and returns to the client code, it needs to be converted back, presumably by a function that finally applies the suffix and prefix. It would be great if we could call that operator StringBuffer() (and even greater if Java, like C++, could apply one user-supplied conversion).

Instead, because there's no convention, we have to give it a name that is necessarily more ambiguous. getStringBuffer() is one obvious name, but to many Java users, that'll imply a corresponding setStringBuffer, which we don't want. Even if it doesn't imply that, the name's ambiguous: is the StringBuffer you're getting the one we operate on, or something else?

toStringBuffer() is a better name, and the pattern I tend to apply, but then someone reading the code wonders why what looks like a getter is called "to"ClassName.

Honestly, except in designing numerical classes or "obviously" cocatenatable objects, there's little use for overloading op+. And as Java isn't value-based like C++, there's not much use for op=; Java isn't trying to make everything act like a primitive int value class. It's op() and the conversion operators I miss.

Upvotes: 0

&#214;zg&#252;r
&#214;zg&#252;r

Reputation: 8247

One of the operators that you had better avoid to overload is conversion operator. It leads to such unexpected results that even STL prefers not to overload it and instead prefers function style conversion:

std::string str = "foo";
char *ch = str.c_str(); //rather than char *ch = str.operator *char();

Upvotes: 0

DaClown
DaClown

Reputation: 4569

It's a nice to have feature but not an essential one. In many cases I find them more confusing than the benefits they have. Like in JaredPars example, the .compare function is more obvious than a '>' to me. One see's directly that point1 is an object and not a primitive data type. I like overloaded operators in libs I use but in my own code I use them very seldom.

EDIT: my choice of the function name is inconsistent. Replace .compare with .greaterThan makes it clearer. What I mean is, that a function name bound to object for me is more obvious than an operator that has no ascociation with the object on the first look. Imho a well chosen function name is easier to read.

Upvotes: -1

Dan Breslau
Dan Breslau

Reputation: 11522

I don't think operator overloading was a bad idea. I do think that making implicit conversion the default behavior was a bad idea. And default implicit conversion in combination with operator overloading is a really bad idea.

Take away implicit conversion entirely -- or make it dependent on an "implicit" keyword -- and the language would never have had the number of potential pitfalls and gotchas discussed in countless articles like this one.

Upvotes: 3

David Thornley
David Thornley

Reputation: 57036

Overloaded operators are potentially excellent ways to do certain things, but are horribly easy to abuse.

Overloading the << and >> operators makes it easy to extend C++'s streams, both in new kinds of streams, new objects for I/O, and both. Overloading -> makes smart pointers almost a drop-in replacement for C++ pointers. Overloaded operators make it possible to have a string concatenation operator, and to build up new sorts of numbers that are syntactically just like ints. Having them makes it possible to do things in libraries that would require language-level changes in other languages.

They do have their limitations. There is no operator suitable for exponentiation. There is only one multiplication operator, and in some cases there's more than one way to multiply (with 3D vectors, for example, there's at least the dot and cross products). The &&, ||, and comma operators cannot replicate their built-in functionality, since they can't have short-circuit evaluations and sequence points.

And, of course, they can be abused. There's no language requirement, for example, that arithmetic operators have to work anything like arithmetic. I've seen horrible things done in an effort to come up with a SQL notation that somebody thought was intuitive. In a C++ program that was badly written, it's impossible to know what, say, a = x * y; does, since it's a.operator=(x.operator*(y));, or maybe a.operator=(operator*(x, y)); or something, and the operator functions could be written to do anything.

Bjarne Stroustrup's intention in designing C++ was to include useful features regardless of the possibility of abuse, whereas James Gosling's intention in designing Java was to exclude excessively abusable features even if they were somewhat useful. It's not clear to me that either of those philosophies is correct or incorrect, but they are different.

Java was designed to avoid situations that would usually call for some C++ features, like operator overloading, multiple inheritance, and run-time type deduction, so they aren't often missed. Whether this is good or bad or neither is not something I know.

As far as teaching students, tell them not to overload operators themselves (except under defined conditions, such as functors and the assignment operator), but point out how the library uses overloaded operators. I wouldn't trust any C++ student to do them right, and if they're going to be able to do it they can and will learn it on their own. They will know it's tricky, because you forbade it in class. Some of the ones I would never trust with anything more complicated than a for statement will find out how to overload operators, and will do anyway, but that's life.

Upvotes: 6

anon
anon

Reputation:

Some examples of overloading that every C++ programmer should know about, even if they don't approve:

  • operator=() is required in order to allow C++ objects to behave like values.
  • operator->() is required in order to implement smart pointers
  • operator<<() and operator>>() are required to integrate types into the iostream framework
  • operator<() is used by default when comparing objects stored in standard library containers
  • operator()() is used to implement functors used by standard library algorithms
  • operator++() is expected to be available if you implement your own iterator

Upvotes: 31

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 247959

Operator overloading is pretty essential for a lot of purposes. Functors would be impossible to create without the ability to overload operator(). Generic programming would in many cases become a pain in the butt. If I write a numerical algorithm, I rely on the value type behaving the same whether it's a float, double, std::complex or some home-brewed type. I rely on the usual arithmetic operators being defined, and such, so I don't have to write a separate overload for built-in types, and another for custom ones.

Smart pointers rely on objects being able to overload the dereferencing operator so that they can behave like pointers.

Operator overloading is extremely important for making c++ programming bearable. As for it being complicated, I just don't see it. It's no more complicated than creating your own function, which people generally find fairly easy. If you name it "multiply" it is a function, if you name it "operator*", it's an operator. But the code in the body is the exact same.

Of course operators sometimes get abused. And << or >> may be borderline acceptable, but they're so commonly known and used that I think it's fair.

If you'd asked about operator overloading in something like C#, however, I'd gladly do without them. Their implementation is much more awkward, they don't work with generics, and they don't enable all the nice and convenient tricks that C++ uses.

Upvotes: 5

timday
timday

Reputation: 24892

I really like the ability to overload arithmetic operators for non-builtin types in C++. But only for types with arithmetic-like behaviour; for example fixed point classes, 3D vector classes, complex number classes, arbitrary length "bignum" classes. I've written similar code in Java and been annoyed by having to write things like a.Add(b) instead of a+b. Mind you, I'm a mathematician by training; seems to me operator overloading lets you get a little bit of domain-specific-language goodness in C++ without having to actually implement one.

But it really annoys me when I see e.g operator+ being overloaded with functionality which would better be done by operator<< (following the dodgy but well established iostream convention) or STL .push_back()-like patterns.

As for operator()... post discovering boost::bind & boost::function I can't imagine life without functors. And smart pointers wouldn't be nearly as convenient without overloaded operator*, operator-> etc.

Upvotes: 1

Eclipse
Eclipse

Reputation: 45493

As easy as it is to complain about overloaded operators, as long as they don't act in suprising ways, I really don't see the issue. Yes there are bad examples out there (even in the stl). See auto_ptr's assignment operator for example. Overloading some operators, like &&, || and , is almost always going to be bad. But for the most part, make the operators do what they advertise and there's no real problem.

It's bad practice to overload operator+ to do something weird, but it's just as bad if you put a method called "Add" to your class that serialized the object to disk.

Upvotes: 14

bayda
bayda

Reputation: 13581

Operators and cases when I used them:
operator->, operator* - for proxy objects and different wrappers.
operator= - needed for avoid unexpected behavior on copy.
operator < (>, <=, >=) - for store in map or set (but usualy better to pass functor into this one ).
operator << ( >> ) - for streams and boost::lexical_cast compatibility.
operator ==, != - for allow compare objects.
operator ! - sometimes instead valid() function.
operator Type - for conversion into other type.
operator() - for smart functor, when boost was disallowed.

that's all.
Sometimes ago I've used other operators, but that was for my mathematic utils.

Also should be careful with logical operators (&&, ||) - we will have difference with standard semantic:

ptr && ptr->method()

could have other sense if we have overloaded operator&&.

Upvotes: 1

Michael Kohne
Michael Kohne

Reputation: 12044

The problem with overloading operators is that some people like to overload them with functionality that doesn't really make any sense in relation to the original (C) purpose of the operator (here I point at the >> and << operators in the std::iostream stuff.). In my mind, the only time you should over-load operators is when either the overload EXACTLY matches the underlying operator's meaning (i.e. < and > must be comparisons.) OR, you must overload it in a certain way in order to interact with another library.

Frankly, I won't overload an operator at all unless I need to for a library. It just makes the reader work too hard to figure out what's going on.

Upvotes: 0

Mark Ransom
Mark Ransom

Reputation: 308148

Overloaded operators are like spice. A little can make something better; too much can make it unpalatable.

Upvotes: 40

Bill the Lizard
Bill the Lizard

Reputation: 405745

As Neil points out in his answer, operator overloading is a necessary topic to learn good object-oriented C++ idioms. I would teach it with a caution to students that if you don't implement overloaded operators following idiomatic conventions, it can lead to very buggy and unexpected behavior. Operator overloading is not a good time to be creative.

Upvotes: 2

Related Questions