The are types we would like to store in standard associative containers
that do not provide a natural operator<, but where a natural
ordering can be defined. We would like to support them having a default
order that is respected by the library, in an API and ABI compatible way
that has no impact on existing code.

The standard associative containers, and many algorithms, use
operator< as the default choice for ordering sequences of arbitrary
data types. This is a good choice for default, serving many types well,
including all types in the standard library with the exception of
std::complex. The question remains, how should a user create a type
that deliberately does not want a mathematical ordering, like
std::complex, but does want to be insertable into a set or
used as the key to a map without requiring the user to explicitly
specify the predicate to be used?

The common advice is that the user should specialize the std::less
template for their purpose, but such a specialization then breaks the contract
that less is a functor that calls operator<, and so such
specializations are technically non-conforming, by not satisfying the original
contract. This problem arises as we are requiring std::less to serve
double-duty, as both the functor analog of the less-than operator, and the
default mechanism to order arbitrary data types.

The conforming choice is to provide an implementation of operator<
that is discovered through ADL. This works by breaking the original design of
the user's class though, so cannot be recommended as the preferred direction.

What is needed is some other functor or function that serves as the default
customization point to describe the ordering behavior of arbitrary types, that
is typically, but not always, going to call operator<.

The key observation is that std::less is not a part of the ABI of any
of the standard containers, but merely a default template parameter. If the
user explicitly specifies their own predicate, then std::less has no
part to play. If the user does not specify a predicate and accepts the
default, then std::less is substituted. Therefore, we can have a
completely backwards compatible update, both API and ABI, as long as it
guarantees to supply std::less as the default template parameter for
any type that would support that today.

Falling back on the fundamental theorem of software engineering attributed to
Andy Koenig, We can solve any problem by introducing an extra level of
indirection. In this case, we want to add a type-computation to find the
default ordering of an arbitrary type, an ordering trait, which
defaults to std::less<T> but can be explicitly specialized by
users for their own types to return a different default:

This technique has been tried and tested on several popular compilers, including
Clang, gcc, and Visual C++, and confirmed to produce identical symbols (ABI) as
the current library.

The same problem is harder to solve for algorithms, as the standard algorithms
do not expose a template-parameter functor computed by default. Instead, they
simply provide two overloads, one that takes a predicate object, and one that
defaults to using operator< directly in the implementation. That
means that even if we have a default ordering for storing elements in a
set, we are still unable to sort a vector of such elements
without calling the sort overload that explicitly requires a predicate
object.

The algorithm problem is still tractable, but messier. If we wish to solve this
problem we need to add constrained overloads to the algorithms, so that types
that can be ordered by operator< continue to be so ordered, but types
that would otherwise fail to compile instead consult the default_order
trait and forward to the predicate version of the algorithm instead, with a
value-initialized predicate of the default ordered type. This can be achieved
in C++14 using SFINAE hackery such as enable_if, but should be more
cleanly specified in C++17 using concepts.

Note that the above formulation for algorithms would still lead to a discrepancy
where types implement both a default order, and define on overloaded
operator<. This could be refined by constraining on types there
is_same_v<default_order_t<T>, less<T>>, at the risk
of ABI breakage. Note that such breakage is strictly opt-in on the part of the
library author choosing to define a different default ordering for their type.

As the issues with the algorithms are a little more involved, this initial
paper does not propose wording, although it should be fairly simple to provide
in an updated paper, given suitable direction by the Library Evolution Working
Group.

While writing this paper, alternatives for providing better defaults in general
were considered. For example, replacing std::less<T> with
std::less<T>. However, all such approaches lead to ABI breakage,
and is some cases source breakage, so were deemed to controversial to propose
at this time. The author would encourage the Library Evolution Working Group to
seriously consider providing transparent functors by default though,
when the possibility of a compatibility-breaking library is under consideration.

The idea of simplifying the overload-sets of algorithms in the library by using
default function template parameters was investigated for C++11 by Doug Gregor in
N2743,
but this was rejected at the time for ABI breaking concerns. This is likely
another topic worth revisiting at such time that the Library Evolution Working
Group is prepared to consider a compatibility-breaking library, but not until.