0

So according to this question it is preferable to implement many operators as external functions rather than member functions. So far I gather that this is because "it makes code more symetrical"... although I haven't yet figured out why that might be advantageous other than it looks nice. I'm guessing when using templates, it means you can get away without writing lots of code?

Anyway, what I wanted to know is: Is it ever appropriate to implement something like operator< as a member function? Or are there no advantages to this?

Reason I ask is because I would never have thought to implement such an operator as an external function. In many examples I have seen before, operators are always implemented as member functions. Is implementing them externally a recent idea which is now considered to be "better"?

(PS: Would someone please clarify why external implementations are the way to go?)

Edit: Actually I have found this link - people seem to disagree about what the best methods is and why.

Community
  • 1
  • 1
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • 1
    One advantage is that `reflexivity`; `obj + 1` and `1 + obj` would be possible if the `+` is not an overloaded member function but an external one or else only `obj + 1` would be possible. – Uchia Itachi Aug 28 '13 at 17:23
  • @UchiaItachi It should be noted that this would require you implemented *both* `operator +(int i, const Type& obj)` and `operator +(const Type& obj, int i)` for both of those to be valid (or have the object expose a compatible cast-operator). Also, return values of arbitrary type to such operators generally require a temporary construction (thank heavens for RVO), whereas `operator <()` as asked by the OP returns a `bool` equivalent. – WhozCraig Aug 28 '13 at 17:32
  • @WhozCraig Yes, agreed. But was answering only the first two sentences of this question :) Anyways thanks for the remainder! – Uchia Itachi Aug 28 '13 at 17:35
  • @WhozCraig You made an excellent point, in that since I have to write both functions anyway, why would I not make them member functions? – FreelanceConsultant Aug 28 '13 at 17:56
  • 1
    @EdwardBird Because you can't make a member `operator <` where the left-hand-side is *not* your instance. – WhozCraig Aug 28 '13 at 17:59
  • @WhozCraig I can if they are both classes. (lhs and rhs are classes, both different though) Although good point in the case of pod types. – FreelanceConsultant Aug 28 '13 at 18:15
  • 1
    @EdwardBird You don't always have to write two "mixed-types" overloads. If the `Type` class has a converting (non-explicit) constructor `Type::Type(int)` you only need to write the "homogeneous" `bool operator<(Type const& a, Type const& b)`, which will be called for the 3 combinations `a_Type < another_Type`, `a_Type < an_int` and `an_int < a_Type` (with the last two implying constructing a temporary `Type` from the `int`; if you want to avoid these implicit conversions you can add the two mixed overloads, as is done for `std::string` with `char const*` to avoid the construction cost). – gx_ Aug 28 '13 at 18:20

1 Answers1

3

The reason that operators are commonly preferred as free functions is that they become type-wise symmetrical. This is more important, or actually important at all, when the type has implicit conversions.

Consider for example an optional type that resembles std::optional from the upcoming C++14 standard, that is implicitly convertible from template argument type, and considering comparing the optional object with the nested type (and imagine that you don't want to implement all of the operator< variants, which the standard actually does):

// optional is like std::optional, implements operator<(optional<T>,optional<T>)
// but we did not care to provide operator<(T,optional<T>) and operator<(optional<T>,T)
optional<int> oi = 1;      // good, we can implicitly convert
if (oi < 10) {             // and we can compare
...
} else if (0 < oi) {       // we also want to compare this way!
...
}

Now if operator< is implemented as a free function, then the code above will work. If it is implemented as a member function the second if will fail, as no conversions can be applied to the left hand side prior to calling a member function.

Is it ever appropriate to implement something like operator< as a member function? Or are there no advantages to this?

There are cases where it won't matter, but it won't provide advantages either. Although this statement is not fully true... lookup is different for the operator being implemented in one way or another, but in the general case it won't matter. If you are in a situation where it matters, then there are deeper issues to care about.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Surely though, if I write external functions to compare two different types, then I have to write one one way round and another the other way round... Picture an example of a 3D-vector comparison with a real number class. (Sorry, silly example because I couldn't think of a better one.) 1:) operator<(vector v, real mod) and 2:) operator<(double mod, real v). I still had to write two functions, might as well have made them member functions, no? – FreelanceConsultant Aug 28 '13 at 17:54
  • 1
    @EdwardBird: Couple of things... first, the type symmetry applies if the type T can be implicitly converted from a different type S. In that case, a single implementation `op<(T,T)` will serve the purpose of `T::op<(T)`, `T::op(S)` and `op(S,T)`. The second thing (which can be noticed by careful reading of the previous alternatives) is that you cannot implement `operator<(double,T)` as a member function at all, since operators implemented as members must be members of the *first* argument (this is the reason for the asymmetry) – David Rodríguez - dribeas Aug 28 '13 at 17:57
  • Ah yes, I have met the second problem before: Trying to multiply a vector by 5: 5 * vector does not work if you implement vector.operator*(double) as a member function. – FreelanceConsultant Aug 28 '13 at 18:14