Herb Sutter's Guru of the Week #4, "Class Mechanics", teaches that the "a op b" form of an overloaded operator should be implemented in terms of the "a op= b" form (see point #4 in the solutions).
As an example, he shows how do to this for the +
operator:
T& T::operator+=( const T& other ) {
//...
return *this;
}
T operator+( T a, const T& b ) {
a += b;
return a;
}
He points out that first parameter in operator+
is intentionally being passed by value, so that it can be moved if the caller passes a temporary.
Notice that this requires that the operator+
be a non-member function.
My question is, how can I apply this technique to an overloaded operator in a CRTP base class?
So say this is my CRTP base class with its operator+=
:
template <typename Derived>
struct Base
{
//...
Derived operator+=(const Derived& other)
{
//...
return static_cast<Derived&>(*this);
}
};
I can see how to implement operator+
in terms of operator+=
as a member function if I dispense with the "pass the first argument by value" optimization:
template <typename Derived>
struct Base
{
//...
Derived operator+(const Derived& other) const
{
Derived result(static_cast<const Derived&>(*this);
result += other;
return result;
}
};
but is there a way to do this while using that optimization (and therefore making the operator+
a nonmember)?
The normal way to implement Herb's advice is as follows:
Extending this to CRTP:
If you try to avoid the use of
friend
here, you realize it's almost this:But is only valid for things derived from
Base<T>
, which is tricky and ugly to do.Additionally, if it's a
friend
internal to the class, then it's not technically in the global namespace. So if someone writes an invalida+b
where neither is aBase
, then your overload won't contribute to the 1000 line error message. The free type-trait version does.As for why that signature: Values for mutable, const& for immutable. && is really only for move constructors and a few other special cases.
If moves are much faster than copies, and you're dealing with a commutative operator, you may be justified in overloading these four. However, it only applies to commutative operators (where A?B==B?A, so + and *, but not -, /, or %). For non-commutative operators, there's no reason to not use the single overload above.