<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US"><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Resolving technical issues in parameter mapping equivalence and related problems</title>

<style type="text/css">
body { color: #000000; background-color: #FFFFFF; max-width: 40em; }
del, .del { text-decoration: line-through; color: #8B0040; }
ins, .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, ul.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.std del, blockquote.std .del, ul.std del, ul.std .del { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC; }
blockquote.std ins, blockquote.std .ins, ul.std ins, ul.std .ins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stdins { text-decoration: none;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3;
  padding-left: 0.5em; padding-right: 0.5em; }

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

table.table { border-spacing: 2px; border-collapse: separate; }
.table * th, .table * td { border: 1px solid black; }

table.frontmatter { border: 0; border-spacing: 0px; border-collapse: collapse; margin: 0; width: 619px; }
.frontmatter * td, .frontmatter * th { padding: 0px; }
.frontmatter * th { font-weight: inherit; text-align: left; vertical-align: top; }

ul.dash { list-style-type: none; }
ul.dash li:before { content: '\2014'; margin-left: -1em }

span.highlight { background-color: #7FDFFF }

.nowrap { white-space: nowrap; }
.pre { white-space: pre; }

pre { font-family: inherit; }

.subtitle { font-size: 80%; }

#xins1:checked ~ * ins { display: none; visibility: hidden }
#xdel1:checked ~ * del { display: none; visibility: hidden }
#xins2:checked ~ * ins { display: none; visibility: hidden }
#xdel2:checked ~ * del { display: none; visibility: hidden }
</style>

<script type="text/javascript" src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>
</head>

<body>
<h1>Resolving technical issues in parameter mapping equivalence and related problems<br
/><span class="subtitle">or: Ordering with simple type equivalence</span></h1>
<table class="frontmatter"><tbody>
    <tr>
        <th>Document number:</th>
        <td>P1624R0</td>
    </tr>
    <tr>
        <th>Date:</th>
        <td>2019-06-17</td>
    </tr>
    <tr>
        <th>Project:</th>
        <td>ISO/IEC JTC 1/SC 22/WG 21/C++</td>
    </tr>
    <tr>
        <th>Audience subgroup:</th>
        <td>Core</td>
    </tr>
    <tr>
        <th>Revises:</th>
        <td><i>None</i></td>
    </tr>
    <tr>
        <th>Reply-to:</th>
        <td><span class="nowrap">Hubert S.K. Tong</span> &lt;<a href="mailto:hubert.reinterpretcast@gmail.com">hubert.reinterpretcast@gmail.com</a>&gt;</td>
    </tr>
</tbody></table>

<h2>Introduction</h2>
<p>As explained in P1375R1, there are technical issues surrounding the definition of equivalence, especially with regards to non-dependent constraint expressions and in the determination of atomic constraint identity.
Non-dependent constraints raise questions regarding what, exactly, they constrain; therefore,
the author wrote P1375R1 based on a principle that partial ordering based on constraints should require a clear relationship between the constraints applied upon the candidates.
Early feedback on that paper was that considering types that are otherwise the same to not be equivalent in the context of atomic constraint identity would be surprising in C++.
The technical problems that originally motivated the exploration into P1375 remains, and this paper attempts to approach the issues from an alternative viewpoint that accepts simple type equivalence.
Some ideas are presented, and the author believes that the expertise of CWG would be helpful in triaging the issues, evaluating the strategy, and in determining what design guidance may be needed.</p>

<h2>Summary of the issues</h2>

<p>The issues basically are:</p>
<ul>
<li>Overload resolution ([over.match.best]) asks us to prefer a more constrained non-template function using rules that order declarations based on their associated constraints ([temp.constr.order]), but
&#x201c;associated constraints&#x201d; are defined for templates ([temp.constr.decl]) and not for functions.</li>
<li>[basic.link] (and other subclauses) do not take trailing <i>requires-clause</i>s into consideration.</li>
<li>Declaration matching ([over.dcl]) is based upon whether trailing <i>requires-clause</i>s are equivalent; however,
<i>equivalent</i>, with respect to expressions ([temp.over.link]), is defined only for expressions involving template parameters.</li>
<li>The equivalence of parameter mappings ([temp.constr.atomic]) is determined by the [temp.over.link] rules for expressions as well.
For types, this makes no sense; and for expressions, this would require distinguishing between expressions that are functionally equivalent but not equivalent.</li>
<li>How template parameters from one template is to be matched against template parameters in another template when they appear in substituted parameter mappings is not clearly defined.</li>
</ul>

<h2>Ideas for resolving the issues</h2>

<h3>Define <i>associated constraints</i> for non-template functions</h3>
<p>In [temp.constr.decl], add a new paragraph:</p>
<blockquote class="stdins"><p>The <i>associated constraints</i> of a non-template function is the normal form of the <i>constraint-expression</i>
introduced by the trailing <i>requires-clause</i>, if any; otherwise, the function has no associated constraints.</p></blockquote>

<p>Substitution of actual template arguments into constraints beyond that necessary for determining satisfaction may be necessary to determine the ordering. For example:</p>
<pre class="example prettyprint language-cpp"><code
>template &lt;typename T&gt; constexpr bool B = true;
template &lt;typename T&gt; concept C = B&lt;T&gt;;

template &lt;typename T&gt;
struct A {
  void f(void) requires true || C&lt;T&gt;;
  int f(void) requires C&lt;int&gt;;
};

int f(A&lt;int&gt; &amp;a) { return a.f(); }</code></pre>

<p>Similarly, for declaration matching:</p>
<pre class="example prettyprint language-cpp"><code
>template &lt;typename T&gt; constexpr bool B = true;
template &lt;typename T&gt; concept C = B&lt;T&gt;;

template &lt;typename T&gt;
struct A { void f(void) requires C&lt;T&gt;; };

template &lt;&gt; void A&lt;int&gt;::f(void) requires C&lt;int&gt;;</code></pre>

<p>Such substitutions may lead to errors.
Since these substitutions are not being performed as part of determining viability of candidates for overload resolution, the SFINAE process does not apply.</p>

<h3>Differentiate functions by their <i>requires-clause</i>s as well</h3>

<p>Various appearances of &#x201c;parameter-type-list&#x201d; in [basic.link], [namespace.udecl], [dcl.link], and [over.load] require updates to take
<i>require-clause</i>s into account.</p>

<h3>Make the determination of expression equivalence treat concept definitions as opaque</h3>
<p>We can extend the definitions of equivalent and functionally equivalent cover expressions subject to normalization in general (not just those involving template parameters).
Then, we add a further condition that an expression that may be subject to constraint normalization is functionally equivalent only if each <i>qualified-concept-name</i>
that may be expanded by normalization would be considered to name the same type if, instead of a concept, a class template was named.</p>

<h3>Change parameter mapping equivalence/substitution</h3>
<p>For non-dependent (after substitution) members of the parameter mapping, consider types by type identity, and expressions by type and value.</p>
<p>For dependent type members of the parameter mapping, treat each layer of substitution as applying an alias template upon the type.
The types are considered the same if declaration matching would consider two template functions parameterized in the same manner as the templates from which the parameter maps originate to be redeclarations if everything about them were the same
except that the <i>parameter-type-list</i> of one consisted of a class template specialization taking one mapping as a type template argument,
and the other consisted of a specialization of the same class template that takes the other mapping as a type template argument.</p>
<p>For dependent non-type members of the parameter mapping, treat each layer of the substitution that does not plainly forward the argument as applying a variable template upon the expression.
Apply the redeclaration-based definition for types, except that the class template instead takes a non-type template argument.</p>
<p>If a template member of the mapping was named as a member of a dependent class, the alias template treatment applies to class.</p>

<h3>Only consider ordering near-identical candidates</h3>
<p>To avoid cases like the following (notice that the correspondence between template parameters between the two candidates differ depending on whether the arguments are deduced or explicitly specified),
further restrictions need to be applied.</p>
<pre class="example prettyprint language-cpp"><code
>template &lt;typename T&gt; constexpr bool B = true;
template &lt;typename T&gt; concept C = B&lt;T&gt;;

template &lt;typename T, typename U&gt; struct A;

template &lt;typename V, typename T, typename U&gt;
int f(V, A&lt;T, U&gt; *) requires C&lt;T&gt;;

template &lt;typename V, typename T, typename U&gt;
void f(V, A&lt;U, T&gt; *) requires C&lt;T&gt; &amp;&amp; C&lt;V&gt; { }

void g(A&lt;short, long&gt; *ap) { return f(0, ap); }</code></pre>

<p>Namely, candidates that are specializations of function templates should be ordered based on their constraints only when the templates have the same name (including for <i>conversion-function-id</i>s), parameter-type-list, and template parameter lists.
This would be consistent with the requirement for non-template functions:</p>
<pre class="example prettyprint language-cpp"><code
>template &lt;typename T&gt; constexpr bool B = true;
template &lt;typename T&gt; concept C = B&lt;T&gt;;

template &lt;typename T&gt;
struct A {
  bool operator==(const A &amp;) const &amp; requires true || C&lt;T&gt;;
};

bool operator==(const A&lt;int&gt; &amp;, const A&lt;int&gt; &amp;) requires C&lt;int&gt; &amp;&amp; true;

bool f(A&lt;int&gt; &amp;a) {
  return a == a;</code> <i>// ambiguous; parameter-type-list</i>s<i> are not the same</i>
}</pre>
</body></html>
