<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

<style type="text/css">
pre {margin-left:20pt; }
pre > i {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
code > i {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
pre > em {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
code > em {
  font-family: "OCR A Extended", "Consolas", "Lucida Console", monospace;
  font-style:italic;
}
body { color: #000000; background-color: #FFFFFF; }
del { text-decoration: line-through; color: #8B0040; }
ins { text-decoration: underline; color: #005100; }

p.example { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract { margin-left: 2em; background-color: #F5F6A2;
  border: 1px solid #E1E28E; }

p.function { }
.attribute { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;
  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

blockquote.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5empadding-right: 0.5em; ; }

blockquote.stdins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; }

table.header { border: 0px; border-spacing: 0;
  margin-left: 0px; font-style: normal; }

table { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.4em; border: none; 
  padding-right: 0.4em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.4em; border: none;
  padding-right: 0.4em; border: none; }
</style>

<title>Change is_transparent to metafunction (Revision 1)</title>
</head>

<body>

<table class="header"><tbody>
  <tr>
    <th>Document number:&nbsp;&nbsp;<th> <td>P0046R1</td>
  </tr>
  <tr>
    <th>Date:&nbsp;&nbsp;<th> <td>2016-01-30</td>
  </tr>
  <tr>
    <th>Project:&nbsp;&nbsp;<th> <td>Programming Language C++, Library Evolution Working Group</td>
  </tr>
  <tr>
    <th>Reply-to:&nbsp;&nbsp;<th> <td><address>Tomasz Kamiński &lt;tomaszkam at gmail dot com&gt;</address></td>
  </tr>
</tbody></table>

<h1><a name="title">Change is_transparent to metafunction (Revision 1)</a></h1>

<h2><a name="intro">Introduction</a></h2>

<p>This proposal discusses an alternative design for enabling a heterogeneous lookup function in the associative
containers, that relies on use of metafunction.</p>

<p>This paper is result of the LWG recommendation provided in the resolution of the <a href="http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4383.html#2430">LWG issue #2430</a>.</p>

<!--h2><a name="toc">Table of contents</a></h2-->

<h2><a name="revision">Revision History</a></h2>

<h3><a name="revision.1">Revision 1</a></h3>

<ul>
  <li>Wording is now relative to <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf">N4567</a> (C++ Working Draft, 2015-11-09).</li>
  <li>Incorporated <code>permits_heterogeneous_lookup_v</code> variable template into main proposal.</li>
  <li>Replaced <code>permits_heterogeneous_lookup&lt;T&gt;::value</code> with <code>permits_heterogeneous_lookup_v&lt;T&gt;</code>.</li>
</ul>

<h2><a name="motivation">Motivation and Scope</a></h2>

<p>In the scope of this paper we propose a new metafunction <code>permits_heterogenous_lookup</code>.
This metafunction is designed to be used to enable the heterogeneous lookup in associative container, instead
of the current standard method relying on presence of <code>is_transparent</code> nested type in the comparator
type ($23.2.4 [associative.reqmts]).</p>

<h3><a name="motivation.backward-compatibility">Preserving backward compatibility</a></h3>

<p>The addition of the heterogeneous lookup to the container may silently change the behavior of the existing code
and, to avoid such situations, it was designed as an opt-in feature. However, the mechanism created for the purpose
is not always working correctly - existing programs may define <code>is_transparent</code> nested type in their
comparator types, and as a consequence the meaning of such programs will be changed.</p>

<p>The introduction of the type trait allows the users to resolve the backward compatibility problem described above
via providing an explicit specialization of the trait that derives from <code>std::false_type</code>. 
It is worth noting that this is only a viable solution for the situation when the programmer does not have the
control over the comparator's code.</p>

<p>Furthermore, it is possible to avoid false positives via providing the default definition
of <code>permits_heterogeneous_lookup</code> that has BaseCharacteristic of <code>std::false_type</code>.
However, such design will no longer support user-defined comparators that intentionally enable new lookup function
by declaring nested type <code>is_transparent</code>.</p>

<p>The resolution of this problem is discussed in <a href="design.backward-compatibility">Backward compatibility</a>
subsection of the proposal.</p>

<h3><a name="motivation.third-party">Support for third party comparators</a></h3>

<p>In the current design enabling heterogeneous lookup requires a modification of the comparator class to 
declare a nested type. As a consequence of this intrusiveness, discussed functionality cannot be enabled
for the third-party types that are outside of their control, even if they support such operations.</p>

<p>In contrast, the use of a type trait allows the new lookup mechanism to be enabled in the program without any
modification to the third party libraries.</p>


<h3><a name="motivation.wrappers">Creating transparent call wrappers</a></h3>

<p>The introduction of the type trait would also simplify the definition of call wrapper types that would like 
to 'inherit' the heterogeneous comparison trait from the wrapped callable.</p>

<p>Suppose we are creating <code>call_wrapper&lt;F&gt;</code>. In the case of the proposed <code>permits_heterogeneous_lookup</code>
trait, the implementation is pretty straight-forward:</p>
<pre>namespace std
{
  template&lt;typename F&gt;
  struct permits_heterogeneous_lookup&lt;call_wrapper&lt;F&gt;&gt; 
    : permits_heterogeneous_lookup&lt;F&gt; {}; 
}</pre>

<p>But for current design it becomes more complicated. The user is required to declare <code>is_transparent</code> nested 
type in the definition of <code>call_wrapper&lt;F&gt;</code> if such type was defined in the <code>F</code> type.</p>
<pre>namespace detail 
{
  template&lt;typename F, typename IT = void&gt;
  struct is_transparent_base {};


  template&lt;typename F&gt;
  struct is_transparent_base&lt;F, void_t&lt;typename F::is_transparent&gt;&gt;
  {
    using is_transparent = typename F::is_transparent; 
  };
}

template&lt;typename F&gt;
struct call_wrapper : detail::is_transparent_base&lt;F&gt;
{ /*<i>...</i>*/ };</pre>

<p>In addition to being longer and intrusive, the nested type implementation requires of type <code>F</code> to 
be complete at the point of instantiation of <code>call_wrapper&lt;F&gt;</code>.
As a consequence, it is impossible to create a reference wrapper class that would support both incomplete types and
heterogeneous container lookup.</p>

<h3><a name="motivation.consistency">Consistency with the Standard</a></h3>

<p>Proposed solution will make this functionality consistent with rest of the standard that relies on the usage
of the traits like: <code>is_placeholder</code> and <code>is_bind_expression</code> in similar situations.</p> 

<h2><a name="design">Design Decisions</a></h2>

<h3><a name="design.semantics">Semantics of the new trait</a></h3>

<p>Existing <code>is_transparent</code> nested was used to mark classes that has one of the following characteristics:</p>
<ol>
  <li>Transparently wraps operation expressed using build-in operators.</li>
  <li>Represents weak total order relation on heterogeneous types and as consequence
  support lookup thought a different type.</li>
</ol>
<p>Although we may point out types that fulfills only one of above criteria. For example diamond version
<code>logical_not</code> or <code>plus</code> are transparently wrapping some build-in operators, 
but usually they cannot be used as comparators. From the other side we may imagine the <code>icase_less</code>
comparator that would provides overloads for <code>std::string</code> and <code>char*</code>.</p>

<p>The trait proposed in this paper is designed to identify only the types in the second category. 
The relation with the existing <code>is_transparent</code> is preserved only for backward compatibility reasons.</p>

<h3><a name="design.naming">Naming</a></h3>

<p>Following criteria was considered in the process of name selection for new metafunction:</p>
<ul>
  <li>The name should not be too generic, to not collide with existing trait or reserve name that
  would be more suitable in other context.
  Names that was rejected because of this criteria: <code>is_generic</code>, <code>is_polymorphic</code>,
  <code>is_transparent</code>.</li>
  <li>The name should match the semantics of the metafunction: if its returns true, the container specialized
  with given functor will expose heterogeneous lookup function, not the functor object itself.
  Names that was rejected because of this criteria: <code>supports_heterogeneous_lookup</code>, 
  <code>provides_heterogeneous_lookup</code>, <code>allows_heterogeneous_lookup</code> and 
  <code>enables_heterogeneous_lookup</code>.</li>
  <li>The name should allow usage of metafunction for future extension that may provide heterogeneous lookup
  for other container types like <code>unordered_set</code>, as consequence it should not refer to concept specific
  to one implementation.
  Names that was rejected because of this criteria: <code>is_heterogeneous_comparator</code>.</li>
  <li>The metafunction represents an opt-in functionality, so it is not automatically enabled for every functor
  thats support heterogeneous comparison. As consequence set of types for which this functionality is enabled is
  subset of types that could be potentially used in this context.
  Names that was rejected because of this criteria: <code>is_compatible_with_heterogeneous_lookup</code>.</li>
</ul>

<p>This paper propose a new name for the trait <code>permits_heterogeneous_lookup</code>, instead of keeping the
name used for nested type. This resolution was selected because the original name (<code>is_transparent</code>)
does not fulfill criteria of a good name listed above.</p>

<h3><a name="design.backward-compatibility">Backward compatibility</a></h3>

<p>As the result of the introduction of <code>is_transparent</code> tag in the C++14 the design of <code>permits_heterogeneous_lookup</code>
must carefully decide to choose one of the following:</p>   
  <ol>
    <li>Fix the pre-C++14 code compatibility problem that may be the result of silently enabling heterogeneous container lookup for
    existing comparators that have coincidentally provided the declaration of <code>is_transparent</code> nested type.
    To achieve that, the default implementation of the metafunction should always return <code>false</code>.</li>
    <li>Preserve the support for post-C++14 user-defined comparator types that intentionally enable heterogeneous
    container lookup via the declaration of <code>is_transparent</code> nested type. To achieve that, the default
    implementation of the metafunction should return <code>true</code> for the functors with this type present.</li>
  </ol>

<p>Considering small possibility of occurrence of compatibility problems and the fact the harm was already done
(C++14 was shipped), the proposal decides to choose second option and provide the default implementation
that relies on presence of the nested type.</p>

<p>As a consequence of this decision, two ways of enabling heterogeneous lookup are provided by the standard:
via the declaration of the nested type, and the explicit specialization of the trait. The author strongly believes
that new mechanism provides an better alternative and decides to express this view via non normative note in
the wording.</p>

<h3><a name="design.header">Place of declaration</a></h3>

<p>The semantics of new <code>permits_heterogeneous_lookup</code> trait is directly related to the associative containers
and it would be preferable to declare it in related header. However currently no common utility header for associative 
container is defined in the standard. Furthermore the scale of the proposed changes does not not justify
introduction of the new header. As consequence we propose that <code>permits_heterogeneous_lookup</code> should be included 
in <code>&lt;functional&gt;</code> header, as it contains definitions of default hash function and comparators that currently
enables heterogeneous lookup.</p>

<h2><a name="standard">Impact On The Standard</a></h2>

<p>This proposal has no dependencies beyond a C++11 compiler and Standard Library implementation.</p>

<p>Nothing depends on this proposal.</p>

<h2><a name="wording">Proposed wording</a></h2>

<p>The proposed wording changes refer to <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf">N4567</a> (C++ Working Draft, 2015-11-09).</p>

<p>After the declaration of <code>hash&lt;T*&gt;</code> in the section 20.9 [function.objects]/2 (Header <code>&lt;functional&gt;</code> synopsis), add:
</p><blockquote class="stdins">
<pre>  // 20.9.14, <em>heterogeneous container lookup;</em>
  template &lt;class F&gt; struct permits_heterogeneous_lookup;</pre>
</blockquote>

<p>After the declaration of <code>is_placeholder_v&lt;T*&gt;</code> in the section 20.9 [function.objects]/2 (Header <code>&lt;functional&gt;</code> synopsis), add:
</p><blockquote class="stdins">
<pre>  // 20.9.14, <em>heterogeneous container lookup;</em>
  template &lt;class F&gt; constexpr bool permits_heterogeneous_lookup_v
    = permits_heterogeneous_lookup&lt;F&gt;::value;</pre>
</blockquote>


<p>After section 20.9.13 Class template <code>hash</code>, insert a new section.</p>
<blockquote class="stdins"> 
<h4>20.9.14 Class template <code>permits_heterogeneous_lookup</code> <span style="float:right">[containers.heterlookup]</span></h4>

<pre>
namespace std {
  template &lt;class F&gt; struct permits_heterogeneous_lookup; // see below
}</pre>

  <dl class="attribute">
    <dd><p>The associative containers defined in [associative] 23.4 use <code>permits_heterogeneous_lookup</code> to detect if member
           functions exposing heterogeneous container lookup should be enabled for a given comparator type.</p></dd>

    <dd><p>Instantiations of the <code>permits_heterogeneous_lookup</code> template shall meet the <code>UnaryTypeTrait</code>
           requirements ([meta.rqmts] 20.10.1).
           The implementation shall provide a definition of <code>permits_heterogeneous_lookup&lt;F&gt;</code> that has a
           <code>BaseCharacteristic</code> of <code>true_type</code> if the <em>qualified-id</em> <code>F::is_transparent</code> 
           is valid and denotes a type ([temp.deduct] 14.8.2),
           otherwise it shall have a <code>BaseCharacteristic</code> of <code>false_type</code>.
           A program may specialize this template for a user-defined type to have a <code>BaseCharacteristic</code> 
           of <code>true_type</code> or <code>false_type</code>.</p></dd>

   <dd><p>[ <em>Note:</em> Specializing <code>permits_heterogeneous_lookup</code> usually provides a better solution than
          declaring <code>is_transparent</code> nested type. <em> — end note</em> ]</p></dd> 

  </dl>
</blockquote>

<p>Change the paragraph 23.2.4 Associative containers [associative.reqmts] p8.</p>

<blockquote class="std"> 

  <dl class="attribute">

    <dd><p>In Table 102,
    <code>X</code> denotes an associative container class,
    <code>a</code> denotes a value of <code>X</code>,
    <code>a_uniq</code> denotes a value of <code>X</code> when <code>X</code> supports unique keys, 
    <code>a_eq</code> denotes a value of <code>X</code> when <code>X</code> supports multiple keys,
    <code>a_tran</code> denotes a value of <code>X</code> when the
    <del><em>qualified-id</em> <code>X::key_compare::is_transparent</code> is valid and denotes a type (14.8.2)</del>
    <ins><code>permits_heterogeneous_lookup_v&lt;typename X::key_compare&gt;</code> is <code>true</code> ([containers.heterlookup] 20.9.14)</ins>,
    <code>i</code> and <code>j</code> satisfy input iterator requirements and refer to elements implicitly convertible to <code>value_type</code>,
    <code>[i,j)</code> denotes a valid range, 
    <code>p</code> denotes a valid const iterator to <code>a</code>,
    <code>q</code> denotes a valid dereferenceable const iterator to <code>a</code>, 
    <code>[q1, q2)</code> denotes a valid range of const iterators in <code>a</code>,
    <code>il</code> designates an object of type <code>initializer_list&lt;value_type&gt;</code>,
    <code>t</code> denotes a value of <code>X::value_type</code>, 
    <code>k</code> denotes a value of <code>X::key_type</code> and 
    <code>c</code> denotes a value of type <code>X::key_compare</code>; 
    <code>kl</code> is a value such that <code>a</code> is partitioned (25.4) with respect to <code>c(r, kl)</code>,
      with <code>r</code> the key value of <code>e</code> and <code>e</code> in <code>a</code>;
    <code>ku</code> is <code>a</code> value such that a is partitioned with respect to <code>!c(ku, r)</code>;
    <code>ke</code> is a value such that <code>a</code> is partitioned with respect to <code>c(r, ke)</code> and <code>!c(ke, r)</code>,
      with <code>c(r, ke)</code> implying <code>!c(ke, r)</code>.
    <code>A</code> denotes the storage allocator used by <code>X</code>, if any, or <code>std::allocator&lt;X::value_type&gt;</code> otherwise,
    and <code>m</code> denotes an allocator of a type convertible to <code>A</code>.</p></dd>

  </dl>

</blockquote>

<h2><a name="feature-testing">Feature-testing recommendation</a></h2>

<p>For the purposes of SG10, we recommend the macro name <code>__cpp_lib_permits_heterogeneous_lookup</code> to be defined in the
<code>&lt;functional&gt;</code> header.</p>

<p>Usage example:</p>
<pre>
namespace third_party
{
  struct icase_less
  {
    bool operator()(std::string const&amp;, std::string const&amp;) const;
    bool operator()(std::string const&amp;, char const*) const;
    bool operator()(char const*, std::string const&amp;) const;
    bool operator()(char const*, char const*) const;
  };
}

#if __cpp_lib_permits_heterogeneous_lookup
namespace std
{
  template&lt;&gt;
  struct permits_heterogeneous_lookup&lt;third_party::icase_less&gt;
    : std::true_type {};
}
#else
struct my_icase_less : third_party::icase_less
{
  using is_transparent = void;
};
#endif</pre>


<h2><a name="acknowledgements">Acknowledgements</a></h2>

<p>Jonathan Wakely provided an improved wording for the <a href="http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4383.html#2430">LWG issue #2430</a>,
   from which this paper originates. Futhermore he has offered many useful suggestions and corrections to the proposal.</p>
<p>Andrzej Krzemieński offered many useful suggestions and corrections to the proposal.</p>
<p>Stephan T. Lavavej suggested numerous corrections to the proposed wording and 
   addition of <code>permits_heterogeneous_lookup_v</code> variable template.</p>


<h2><a name="literature">References</a></h2>

<ol>
<li>Marshall Clow, "C++ Standard Library Active Issues List (Revision R92)" (N4383, <a href="http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4383.html">http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4383.html</a>)</li>
<li>Richard Smith, "Working Draft, Standard for Programming Language C++" (N4567, <a href="http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf">http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2015/n4567.pdf</a>)</li>
</ol>

</body></html>
