Is this code possible to realize?
Yes. But you really, really, really should not do it! operator<
has a very concise meaning: Is the left hand side "less" (for some definition of "less") than the right hand side?
Returning the object which is actually "less" is also a reasonable function, but it should be named accordingly! Call it lesser_of
or something like that.
Not working right now?
You did not include any useful error description in your question, but I highly suspect that the issue here is const
correctness. You're accepting a reference to a const
qualified Foo
and try to call its member function operator<
which is not const
qualified. The simple fix: Add a const
to the member function:
// member function: vvvvv
bool operator<(const Foo& otherFoo) const {
// implementation ^^^^^
}
The supposed free (= non-member) function (which should really be called with a reasonable name, I chose lesser_of
) can be implemented for all types with a corresponding member function, using a template:
template<typename L, typename R>
typename std::common_type<L,R>::type lesser_of(L&& left, R&& right) {
using std::forward;
// Could also use operator<(forward<L>(left), forward<R>(right)), but
// this breaks when there's both a member function and a free
// function available.
if (left.operator<(forward<R>(right))) {
return forward<L>(left);
} else {
return forward<R>(right);
}
}
Note that I have no idea whether the forwarding makes any sense, nor am I sure if this could lead to dangling references.
Going a bit further, there's one kind of usage that I would consider "only just ok" if you really insist on returning "more" than a bool
from operator<
: In Common Lisp this is called "generalized boolean", and basically means that anything except a special nil
value is considered to be "truthy". You could "port" that to C++ and use std::optional
(C++17!) to convey that meaning:
template<typename L, typename R>
std::experimental::optional<L> operator<(L&& left, R&& right) {
if (left.operator<(std::forward<R>(right))) {
return std::forward<L>(left);
} else {
return std::experimental::nullopt_t{};
}
}
This returns the left operand wrapped in a std::optional
if indeed it is "less" than the right. The result can be checked for in e.g. an if
(or similar "special context") because it has a (explicit
) conversion member function to bool
. Thus returning std::optional
instead of bool
won't break code that uses the comparison only where contextual conversions can be applied. The returned value can be access by dereferencing or for example the value
member function.
Of course this does not allow code like Foo c = a < b;
, but you could instead use Foo c = (a < b).value_or(b);
.