Categories
c++ c++-faq operator-overloading operators

What are the basic rules and idioms for operator overloading?

2377

Note: The answers were given in a specific order, but since many users sort answers according to votes, rather than the time they were given, here’s an index of the answers in the order in which they make the most sense:

(Note: This is meant to be an entry to Stack Overflow’s C++ FAQ. If you want to critique the idea of providing an FAQ in this form, then the posting on meta that started all this would be the place to do that. Answers to that question are monitored in the C++ chatroom, where the FAQ idea started in the first place, so your answer is very likely to get read by those who came up with the idea.)

9

  • 72

    If we’re going to continue with the C++-FAQ tag, this is how entries should be formatted.

    Dec 14, 2010 at 19:45

  • 1

    I’ve written a short series of articles for the german C++ community about operator overloading: Part 1: operator overloading in C++ covers semantics, typical usage and specialities for all operators. It has some overlappings with your answers here, nevertheless there is some additional information. Parts 2 and 3 make an tutorial for using Boost.Operators. Would you like me to translate them and add them as answers?

    Apr 9, 2013 at 14:11

  • 1

    Oh, and an English translation is also available: the basics and common practice

    Jul 31, 2017 at 14:13

  • The address-of operator operator& is missing.

    – Red.Wave

    Jan 19, 2021 at 12:36

  • @Red.Wave: Actually, there’s a sentence, even in its own paragraph, at the end of the common operator answer, but it says “don’t do this”. I think it was Pete Becker of Dinkumware (the company which made the std lib later bought by Microsoft) who once said that those who overload operator&() and then expect the resulting types to work with the standard library should be forced to implement a std lib which performs this miracle. IOW, if you think you have an application for overloading this operator, I’d be curious to hear it. (Don’t hold your breath waiting for applause, though.)

    – sbi

    Mar 30, 2021 at 20:25

537

The Three Basic Rules of Operator Overloading in C++

When it comes to operator overloading in C++, there are three basic rules you should follow. As with all such rules, there are indeed exceptions. Sometimes people have deviated from them and the outcome was not bad code, but such positive deviations are few and far between. At the very least, 99 out of 100 such deviations I have seen were unjustified. However, it might just as well have been 999 out of 1000. So you’d better stick to the following rules.

  1. Whenever the meaning of an operator is not obviously clear and undisputed, it should not be overloaded. Instead, provide a function with a well-chosen name.
    Basically, the first and foremost rule for overloading operators, at its very heart, says: Don’t do it. That might seem strange, because there is a lot to be known about operator overloading and so a lot of articles, book chapters, and other texts deal with all this. But despite this seemingly obvious evidence, there are only a surprisingly few cases where operator overloading is appropriate. The reason is that actually it is hard to understand the semantics behind the application of an operator unless the use of the operator in the application domain is well known and undisputed. Contrary to popular belief, this is hardly ever the case.

  2. Always stick to the operator’s well-known semantics.
    C++ poses no limitations on the semantics of overloaded operators. Your compiler will happily accept code that implements the binary + operator to subtract from its right operand. However, the users of such an operator would never suspect the expression a + b to subtract a from b. Of course, this supposes that the semantics of the operator in the application domain is undisputed.

  3. Always provide all out of a set of related operations.
    Operators are related to each other and to other operations. If your type supports a + b, users will expect to be able to call a += b, too. If it supports prefix increment ++a, they will expect a++ to work as well. If they can check whether a < b, they will most certainly expect to also to be able to check whether a > b. If they can copy-construct your type, they expect assignment to work as well.


Continue to The Decision between Member and Non-member.

44

  • 17

    The only thing of which I am aware which violates any of these is boost::spirit lol.

    Dec 12, 2010 at 17:50

  • 76

    @Billy: According to some, abusing + for string concatenation is a violation, but it has by now become well established praxis, so that it seems natural. Although I do remember a home-brew string class I saw in the 90ies that used binary & for this purpose (referring to BASIC for established praxis). But, yeah, putting it into the std lib basically set this in stone. The same goes for abusing << and >> for IO, BTW. Why would left-shifting be the obvious output operation? Because we all learned about it when we saw our first “Hello, world!” application. And for no other reason.

    – sbi

    Dec 12, 2010 at 19:56


  • 6

    @curiousguy: If you have to explain it, it’s not obviously clear and undisputed. Likewise if you need to discuss or defend the overloading.

    – sbi

    Dec 2, 2011 at 12:09


  • 6

    @sbi: “peer review” is always a good idea. To me a badly choosen operator is not different from a badly choosen function name (I saw many). Operator are just functions. No more no less. Rules are just the same. And to understand if an idea is good, the best way is understand how long does it takes to be understood. (Hence, peer review is a must, but peers must be chosen between people free from dogmas and prejudice.)

    Apr 9, 2012 at 16:57

  • 6

    @sbi To me, the only absolutely obvious and indisputable fact about operator== is that it should be an equivalence relation (IOW, you should not use non signaling NaN). There are many useful equivalence relations on containers. What does equality means? “a equals b” means that a and b have the same mathematical value. The concept of mathematical value of a (non-NaN) float is clear, but the mathematical value of a container can have many distinct (type recursive) useful definitions. The strongest definition of equality is “they are the same objects”, and it is useless.

    Apr 10, 2012 at 0:49

281

The Decision between Member and Non-member

The binary operators = (assignment), [] (array subscription), -> (member access), as well as the n-ary () (function call) operator, must always be implemented as member functions, because the syntax of the language requires them to.

Other operators can be implemented either as members or as non-members. Some of them, however, usually have to be implemented as non-member functions, because their left operand cannot be modified by you. The most prominent of these are the input and output operators << and >>, whose left operands are stream classes from the standard library which you cannot change.

For all operators where you have to choose to either implement them as a member function or a non-member function, use the following rules of thumb to decide:

  1. If it is a unary operator, implement it as a member function.
  2. If a binary operator treats both operands equally (it leaves them unchanged), implement this operator as a non-member function.
  3. If a binary operator does not treat both of its operands equally (usually it will change its left operand), it might be useful to make it a member function of its left operand’s type, if it has to access the operand’s private parts.

Of course, as with all rules of thumb, there are exceptions. If you have a type

enum Month {Jan, Feb, ..., Nov, Dec}

and you want to overload the increment and decrement operators for it, you cannot do this as a member functions, since in C++, enum types cannot have member functions. So you have to overload it as a free function. And operator<() for a class template nested within a class template is much easier to write and read when done as a member function inline in the class definition. But these are indeed rare exceptions.

(However, if you make an exception, do not forget the issue of const-ness for the operand that, for member functions, becomes the implicit this argument. If the operator as a non-member function would take its left-most argument as a const reference, the same operator as a member function needs to have a const at the end to make *this a const reference.)


Continue to Common operators to overload.

34

  • 11

    Herb Sutter’s item in Effective C++ (or is it C++ Coding Standards?) says one should prefer non-member non-friend functions to member functions, to increase the encapsulation of the class. IMHO, the encapsulation reason takes precedence to your rule of thumb, but it does not decrease the quality value of your rule of thumb.

    – paercebal

    Dec 12, 2010 at 13:36

  • 11

    @paercebal: Effective C++ is by Meyers, C++ Coding Standards by Sutter. Which one are you referring to? Anyway, I dislike the idea of, say, operator+=() not being a member. It has to change its left-hand operand, so by definition it has to dig deep into its innards. What would you gain by not making it a member?

    – sbi

    Dec 12, 2010 at 13:39


  • 10

    @sbi: Item 44 in C++ Coding Standards (Sutter) Prefer writing nonmember nonfriend functions, of course, it only applies if you can actually write this function using only the public interface of the class. If you cannot (or can but it would hinder performance badly), then you have to make it either member or friend.

    Dec 12, 2010 at 15:45

  • 4

    @sbi : Oops, Effective, Exceptional… No wonder I mix the names up. Anyway the gain is to limit as much as possible the number of functions that have access to an object private/protected data. This way, you increase the encapsulation of your class, making its maintenance/testing/evolution easier.

    – paercebal

    Dec 12, 2010 at 16:51

  • 13

    @sbi : One example. Let’s say you’re coding a String class, with both the operator += and the append methods. The append method is more complete, because you can append a substring of the parameter from index i to index n -1: append(string, start, end) It seems logical to have += call append with start = 0 and end = string.size. At that moment, append could be a member method, but operator += doesn’t need to be a member, and making it a non-member would decrease the quantity of code playing with the String innards, so it is a good thing…. ^_^ …

    – paercebal

    Dec 12, 2010 at 16:58