<html>

<head>
  <meta name="description" content="Proposal for C++ Standard">
  <meta name="keywords" content="C++,cplusplus,wg21">
  <meta name="author" content="Alisdair Meredith">

  <title>Ordered By Default</title>

  <style type="text/css">
    ins {background-color:#A0FFA0}
    del {background-color:#FFA0A0}
    p {text-align:justify}
    li {text-align:justify}
    blockquote.note
    {
      background-color:#E0E0E0;
      padding-left: 15px;
      padding-right: 15px;
      padding-top: 1px;
      padding-bottom: 1px;
    }
  </style>
</head>

<body>
<table>
<tr>
  <td align="left">Doc. no.</td>
  <td align="left">P0181R0</td>
</tr>
<tr>
  <td align="left">Date:</td>
  <td align="left">2016-02-17</td>
</tr>
<tr>
  <td align="left">Project:</td>
  <td align="left">Programming Language C++</td>
</tr>
<tr>
  <td align="left">Audience:</td>
  <td align="left">Library Evolution Working Group</td>
</tr>
<tr>
  <td align="left">Reply to:</td>
  <td align="left">Alisdair Meredith &lt;<a href="mailto:ameredith1@bloomberg.net">ameredith1@bloomberg.net</a>&gt;</td>
</tr>
</table>

<h1>Ordered By Default</h1>

<h2>Table of Contents</h2>
<ul>
<li><a href="#rev.hist">Revision History</a></li>
  <ul>
  <li><a href="#rev.0">Revision 0</a></li>
  </ul>
<li><a href="#1.0">1 Introduction</a></li>
<li><a href="#2.0">2 Stating the problem</a></li>
  <ul>
  <li><a href="#2.1">Consistency of propagation traits</a></li>
  </ul>
<li><a href="#3.0">3 Propose Solution</a></li>
<li><a href="#4.0">4 Other Directions</a></li>
<li><a href="#5.0">5 Formal Wording</a></li>
<li><a href="#6.0">6 Acknowledgements</a></li>
<li><a href="#7.0">7 References</a></li>
</ul>


<h2><a name="rev.hist">Revision History</a></h2>

<h3><a name="rev.0">Revision 0</a></h3>
<p>
Original version of the paper for the 2016 pre-Jacksonville mailing.
</p>

<h6>Sketch of ideas to be turned into paper</h6>
<p>
This is a quick checklist of ideas that need to be incorporated.
</p>
<ul>
  <li>Write introduction</li>
  <li>Describe Problem - operator&lt; is not the perfect default</li>
  <li>Propose Solution - indirection through default template parameter</li>
  <li>Describe any alternatives - std2</li>
  <li>Write Wording</li>
</ul>


<h2><a name="1.0">1 Introduction</a></h2>
<p>
The are types we would like to store in standard associative containers
that do not provide a natural <tt>operator&lt;</tt>, 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.
</p>


<h2><a name="2.0">2 Stating the problem</a></h2>
<p>
The standard associative containers, and many algorithms, use
<tt>operator&lt;</tt> 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
<tt>std::complex</tt>.  The question remains, how should a user create a type
that deliberately does not want a mathematical ordering, like
<tt>std::complex</tt>, but does want to be insertable into a <tt>set</tt> or
used as the key to a <tt>map</tt> without requiring the user to explicitly
specify the predicate to be used?
</p>

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

<p>
The conforming choice is to provide an implementation of <tt>operator&lt;</tt>
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.
</p>

<p>
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 <tt>operator&lt;</tt>.
</p>

<h3><a name="3.0">3 Propose Solution</a></h3>
<p>
The key observation is that <tt>std::less</tt> 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 <tt>std::less</tt> has no
part to play.  If the user does not specify a predicate and accepts the
default, then <tt>std::less</tt> is substituted.  Therefore, we can have a
completely backwards compatible update, both API and ABI, as long as it
guarantees to supply <tt>std::less</tt> as the default template parameter for
any type that would support that today.
</p>

<p>
Falling back on the fundamental theorem of software engineering attributed to
Andy Koenig, <em>We can solve any problem by introducing an extra level of
indirection</em>.  In this case, we want to add a type-computation to find the
default ordering of an arbitrary type, an <em>ordering trait</em>, which
defaults to <tt>std::less&lt;T&gt;</tt> but can be explicitly specialized by
users for their own types to return a different default:
</p>
<pre>
template &lt;typename T&gt;
struct default_order {
   using type = std::less&lt;T&gt;;
};

template &lt;typename T&gt;
using default_order_t = typename default_order&lt;T&gt;::type;

template &lt;&gt;
struct default_order&lt;MyType&gt; {
   using type = MyOrderingComparator;
};
</pre>

<p>
We can then substitute this type-computation as the default template parameter
for the predicate in associative containers:
</p>
<pre>
template &lt;class Key, class T, class Compare = default_order_t&lt;Key&gt;,
          class Allocator = allocator&lt;pair&lt;const Key, T&gt;&gt;&gt;
class map;

template &lt;class Key, class T, class Compare = default_order_t&lt;Key&gt;,
          class Allocator = allocator&lt;pair&lt;const Key, T&gt;&gt;&gt;
class multimap;

template &lt;class Key, class Compare = default_order_t&lt;Key&gt;,
          class Allocator = allocator&lt;Key&gt;&gt;
class set;

template &lt;class Key, class Compare = default_order_t&lt;Key&gt;,
          class Allocator = allocator&lt;Key&gt;
class multiset;
</pre>

<p>
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.
</p>

<p>
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 <tt>operator&lt;</tt> directly in the implementation.  That
means that even if we have a default ordering for storing elements in a
<tt>set</tt>, we are still unable to <tt>sort</tt> a vector of such elements
without calling the <tt>sort</tt> overload that explicitly requires a predicate
object.
</p>

<p>
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 <tt>operator&lt;</tt> continue to be so ordered, but types
that would otherwise fail to compile instead consult the <tt>default_order</tt>
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 <tt>enable_if</tt>, but should be more
cleanly specified in C++17 using concepts.
</p>

<p>
Note that the above formulation for algorithms would still lead to a discrepancy
where types implement both a default order, <em>and</em> define on overloaded
<tt>operator&lt;</tt>.  This could be refined by constraining on types there
<tt>is_same_v&lt;default_order_t&lt;T&gt;, less&lt;T&gt;&gt;</tt>, 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.
</p>

<p>
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.
</p>

<h3><a name="4.0">4 Other Directions</a></h3>
<p>
While writing this paper, alternatives for providing better defaults in general
were considered.  For example, replacing <tt>std::less&lt;T&gt;</tt> with
<tt>std::less&lt;T&gt;</tt>.  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 <em>transparent</em> functors by default though,
when the possibility of a compatibility-breaking library is under consideration.
</p>

<p>
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
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2743">N2743</a>,
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.
</p>

<h3><a name="5.0">5 Formal Wording</a></h3>
<p>
Make the following changes to the specified working paper:
</p>

<blockquote>

<h4>23.4.2 Header &lt;map&gt; synopsis [associative.map.syn]</h4>
<h5>Header <functional> synopsis</h5>
<blockquote><pre>
namespace std {
  <i>// ... elide header details and insert at the end</i>

  <ins><i>// 20.9.X, Default Functor Traits</i></ins>
  <ins>template &lt;typename T&gt;</ins>
  <ins>struct default_order {</ins>
      <ins>using type = std::less&lt;T&gt;;</ins>
  <ins>};</ins>

  <ins>template &lt;typename T&gt;</ins>
  <ins>using default_order_t = typename default_order&lt;T&gt;::type;</ins>  
}
</pre></blockquote>


<h4>23.4.2 Header &lt;map&gt; synopsis [associative.map.syn]</h4>
<blockquote><pre>
namespace std {
  <i>// 23.4.4, class template map:</i>
  template &lt;class Key, class T, class Compare = <ins>default_order_t</ins><del>less</del>&lt;Key&gt;,
            class Allocator = allocator&lt;pair&lt;const Key, T&gt;&gt;&gt;
  class map;

  <i>// 23.4.5, class template multimap:</i>
  template &lt;class Key, class T, class Compare = <ins>default_order_t</ins><del>less</del>&lt;Key&gt;,
            class Allocator = allocator&lt;pair&lt;const Key, T&gt;&gt;&gt;
  class multimap;
}
</pre></blockquote>

<h4>23.4.3 Header &lt;set&gt; synopsis [associative.set.syn]</h4>
<blockquote><pre>
namespace std {
  <i>// 23.4.6, class template set:</i>
  template &lt;class Key, class Compare = <ins>default_order_t</ins><del>less</del>&lt;Key&gt;,
            class Allocator = allocator&lt;Key&gt;&gt;
  class set;


  <i>// 23.4.6, class template multiset:</i>
  template &lt;class Key, class Compare = <ins>default_order_t</ins><del>less</del>&lt;Key&gt;,
            class Allocator = allocator&lt;Key&gt;
  class multiset;
}
</pre></blockquote>

</blockquote>



<h2><a name="6.0">6 Acknowledements</h2>
<p>
Thanks to Tony Van Eerd for repeatedly encouraging me to write this paper, and
Howard Hinnant for encouraging experiments to confirm that the ABI claims hold
up in practice.
</p>


<h2><a name="7.0">7 References</h2>
<ul>
  <li>
    <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2743">N2743</a>
    Unifying Operator and Function-Object Variants of Standard Library Algorithms, Douglas Gregor
  </li>
</ul>


</body>
</html>
