<html><head>
  <style type="text/css">
<style type="text/css">
	

body { color: #000000; background-color: #FFFFFF; }
ins, ins * { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
del, del * { text-decoration:line-through; background-color:#FFA0A0 }
#hidedel:checked ~ * del, #hidedel:checked ~ * del * { display:none; visibility:hidden }

p, li, blockquote, input {
    font-family: "sans", Times, serif;
    font-size: 11pt
}
h1, h2, h3 {font-family: "sans", Times, serif;}
h4 {font-family: "sans", Times, serif; margin-top:0.25em;}

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: #FFFFFF;
  border: 1px solid #E1E28E; font-family: "monospace"; }

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 { 
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; padding-top:0; }

blockquote pre em { font-family: 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.8em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }
  

ul { list-style-type: none; }
a { text-decoration: none; }

ul > li:before {
	content: "\2014";
	position: absolute;
	margin-left: -1.5em; 
}


p.indented { margin-left:8em; margin-top:0; margin-bottom: 0; text-indent:-4em }
p.grammarlhs { font-style:italic; margin-bottom: 0; margin-top: 0em }
p.grammarrhs { font-style:italic; margin-left:8em; margin-top:0; margin-bottom: 0; text-indent:-4em }
p.grammarrrhs { font-style:italic; margin-left:12em; margin-top:0; margin-bottom: 0; text-indent:-4em }
p.grammarlhs tt { font-style:normal }
p.grammarrhs tt { font-style:normal }
p.grammarrrhs tt { font-style:normal }

sup.supsub {position:relative; left: -.0em; bottom: .2em;}
sub.supsub {position:relative; bottom: -.1em;}

  </style>
  <title>Proposal: conversions to arrays of unknown bound</title>
</head><body>
<div style="text-align: right; float: right;">
<p>ISO/IEC JTC1 SC22 WG21, Core Working Group<br>
P0388R4<br>
Robert Haberlach (r.hl{at}gmx{dot}net)<br>
Richard Smith (richard@metafoo.co.uk)<br>
2019-07-18</p>
</div>

<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/styles/idea.min.css">
<script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.4.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
	
	<h2>Permit conversions to arrays of unknown bound</h2>
	<p>Permit conversions of arrays of known bound to pointers or references to arrays of unknown bound. This is analogous to evolution issue 118.<sup><a href="#EWG118">EWG118</a></sup></p>
	
	<h4>Changes from R3</h4>
	Rebased on latest working draft and added context to "delete 8.2.2p3" editing instruction.
	
	<h4>Changes from R2</h4>
	The proposed wording change for 7.5 [conv.qual] contained a few bogus references, which were corrected. 
	
	<h4>Changes from R1</h4>
	Incorporated the suggestions made by CWG in Albuquerque.
	<ul>
<li>Grammar and punctuation fixes.</li>
<li>Include a discussion as to the impacts.</li>
</ul>
	
	<h4>Changes from R0</h4>
	Incorporated the suggestions made by CWG in Kona/Toronto.
	<ul><li>Enable brace-elision by having [dcl.init.list] ¶(3.8) deduce the type off a declaration with a corresponding initializer.</li>
	<li>Instead of creating a new paragraph 13.3.3.2 ¶5, add a bullet to ¶(3.1).</li>
	<li>The rules were adjusted to match better the one introduced by core issue 1307; the arrays now need to have the same element type for the tie-breaker in [over.ics.rank] to apply. </li>
	<li>Allow more general pointer conversions by incorporating the bound conversion into qualification conversions.</li></ul>
	
	<h3>Motivation and impact</h3>
	<p>As of core issue 393,<sup><a href="#CWG393">CWG393</a></sup> function parameters can be pointers or references to arrays of unknown bound.
	 However, binding such a parameter to an array of known bound isn't permitted:</p>

<pre class="extract"><code>void f(int(&)[]);
int arr[1];

f(arr);          // Error
int(&r)[] = arr; // Error
</code></pre>
<p>This restriction is unjustified and should be removed. One consequence of our approach is the fact that </p>
<pre class="extract"><code>struct A {
    A();
    A(const A(&)[2]);
};

using T = A[];
using U = A[2];

A (&&t)[] = {U{}};
</code></pre>
  <p>changes meaning: at the moment, <tt>t</tt> binds indirectly to a temporary <tt>A{U{}}</tt>, while under the new type of reference relation, it binds directly to <tt>U{}</tt>. This is can be deemed negligible.
    It was considered during Alberquerque whether binding directly could be the intent when arrays of known but incompatible bound are matched, where the initialiser is enclosed in <tt>{}</tt>. If so, we could alter the definition of reference-relation to incorporate arrays of all bounds and 
  check that they are compatible at a later stage. However, when the reference is to an array of known bound, the scenario could be along the lines of </p>
  <pre class="extract"><code>namespace json {

  struct Value {
    Value();
    template<int N>
    Value(const Value(&)[N]);
  };

  using OneTuple = Value[1];
  using TwoTuple = Value[2];

  OneTuple const&a = {TwoTuple{}};

}
</code></pre>
&hellip; which we'd like to preserve as binding indirectly. (Thanks to Richard for the example.)

	<h3>Approach and decisions</h3>
	<p>The initialization of pointers to arrays of unknown bound will be allowed by extending qualification conversions to drop bounds.
	Reference initialization rules will be adjusted by modifying reference relation. Such bindings have Exact Match rank.<br>
	
	We also propose to allow list-initialization for references to arrays of unknown bound by deducing the array temporary's size. </p>
	
	<h4>Ranking of reference initialization conversions</h4>
	<p>Consider</p>
	
<pre class="extract"><code>void f(int(&)[]),      // (1)
     f(int(&)[1]),     // (2)
     f(int*);          // (3)

void h(int(*)[]),      // (a)
     h(int(*)[1]);     // (b)
</code></pre>

<p>(2) and (b) should clearly be better than (1) and (a), respectively, as the former overloads are more restricted. <br><br>

Furthermore, (3) should be equal to (1) (as it is to (2)). To maintain this ambiguity, (1) must be given Exact Match rank. We do not favor this ordering and feel that (3) is conceptually
less specialized than (2) and even (1); however, consistency is more important than fixing part of the problem for arrays of unknown bounds.<br><br>

Finally, (a) should be worse than (b), which is implied by (a) necessitating a qualification conversion in that case.</p>

<h4>Ranking of list-initialization sequences</h4>
<p>We also propose to allow list-initialization and introduce corresponding rules in overload resolution:</p>
<pre class="extract"><code>int b(int   (&&)[] );   // #1
int b(long  (&&)[] );   // #2

int b(int   (&&)[1]);   // #3
int b(long  (&&)[1]);   // #4

int b(int   (&&)[2]);   // #5

b({1});</code></pre>
<p>Here,</p>
<ul><li>#1, #3 and #5 should rank better than both #2 and #4, as no promotion is necessitated. This is consistent with the approach of core issue 1307,<sup><a href="#CWG1307">CWG1307</a></sup> whose resolution this paper extends. </li>
<li>#1 should rank worse than #3, being far less specialized.</li>
<li>#1 should rank better than #5, as the latter requires a larger array temporary.<br>
(#3 also ranks better than #5 for the same reason--cf. core issue 1307).</li></ul>
<p>For these reasons, only if the arrays are of the same type, the size of the referenced array and the known/unknown are primary criterions, (unconditionally) followed by the worst performed conversion.</p>
	
	<h3>Proposed wording</h3>
	
	<p>This is based on N4820.</p>
	
<input type="checkbox" id="hidedel">Hide deleted wording</input>


<p>Move 7.2.2 [expr.type] &para;3 ("The <i>cv-combined type</i> of two types [...]") to the beginning of 7.3.5 [conv.qual] &para; 3, and modify the resulting 7.3.5 [conv.qual] as follows:</p>
	<blockquote class="std"><ol><li>A <em>cv</em><em>-decomposition</em> of a type <tt>T</tt> is a sequence of 
	<em>cv<sub>i</sub></em> and <em>P<sub>i</sub></em> such that <tt>T</tt> is <br><br>

		<p class="indented">&ldquo;<em>cv<sub>0</sub></em> <em>P<sub>0</sub></em> <em>cv<sub>1</sub></em> <em>P<sub>1</sub></em> &hellip; <em>cv<sub>n-1</sub></em> <em>P<sub>n-1</sub></em> <em>cv<sub>n</sub></em> <tt>U</tt>&rdquo; for <em>n > 0</em>,</p><br>

	where each <em>cv<sub>i</sub></em> is a set of cv-qualifiers (6.9.3), and each <em>P<sub>i</sub></em> is &ldquo;pointer to&rdquo; (11.3.1), &ldquo;pointer to member of
	class <em>C<sub>i</sub></em> of type&rdquo; (11.3.3), &ldquo;array of <em>N<sub>i</sub></em>&rdquo;, or &ldquo;array of unknown bound of&rdquo; (11.3.4). If <em>P<sub>i</sub></em> designates an array,
	the cv-qualifiers <em>cv<sub>i+1</sub></em> on the element type are also taken as the cv-qualifiers <em>cv<sub>i</sub></em> of the array. [ <em>Example</em>: The
	type denoted by the <em>type-id</em> <tt>const int **</tt> has two cv-decompositions, taking <tt>U</tt> as &ldquo;int&rdquo; and as &ldquo;pointer to
	<tt>const int</tt>&rdquo;. — <em>end example</em> ] The <em>n</em>-tuple of cv-qualifiers after the first one in the longest cv-decomposition
	of <tt>T</tt>, that is, <em>cv<sub>1</sub></em>, <em>cv<sub>2</sub></em>, &hellip;, <em>cv<sub>n</sub></em>, is called the <em>cv-qualification signature</em> of <tt>T</tt>.</li>
	
	<li>Two types <tt>T</tt><sub>1</sub> and <tt>T</tt><sub>2</sub> are <em>similar</em> if they have cv-decompositions with the same <em>n</em> such that corresponding <em>P<sub>i</sub></em>
components are <ins>either</ins> the same <ins>or one is &ldquo;array of <em>N<sub>i</sub></em>&rdquo; and the other is &ldquo;array of unknown bound of&rdquo;,</ins> and the types denoted by <tt>U</tt> are the same.</li>

	<li>The <em>cv-combined type</em> of two types <tt>T</tt><sub>1</sub> and <tt>T</tt><sub>2</sub> is <del>a</del><ins>the</ins> type <tt>T</tt><sub>3</sub> similar to <tt>T</tt><sub>1</sub> whose <del>cv-qualification signature</del> <ins>cv-decomposition</ins> 
is such that:<br>
<ul>
<li>for every <em>i > 0</em>, 
<ul>
<li><em>cv<sub class="supsub">i</sub><sup class="supsub">3</sup></em> is the union of <em>cv<sub class="supsub">i</sub><sup class="supsub">1</sup></em> and <em>cv<sub class="supsub">i</sub><sup class="supsub">2</sup></em>;</li>
<li><ins>if either <em>P<sub class="supsub">i</sub><sup class="supsub">1</sup></em> or <em>P<sub class="supsub">i</sub><sup class="supsub">2</sup></em> is &ldquo;array of unknown bound of&rdquo;, <em>P<sub class="supsub">i</sub><sup class="supsub">3</sup></em> is &ldquo;array of unknown bound of&rdquo;, otherwise it is <em>P<sub class="supsub">i</sub><sup class="supsub">1</sup></em>;</ins> </li>
</li>
<li>if the resulting <em>cv<sub class="supsub">i</sub><sup class="supsub">3</sup></em> is different from <em>cv<sub class="supsub">i</sub><sup class="supsub">1</sup></em> or <em>cv<sub class="supsub">i</sub><sup class="supsub">2</sup></em>, 
<ins>or the resulting <em>P<sub class="supsub">i</sub><sup class="supsub">3</sup></em> is different from <em>P<sub class="supsub">i</sub><sup class="supsub">1</sup></em> or <em>P<sub class="supsub">i</sub><sup class="supsub">2</sup></em>,</ins>
then <tt>const</tt> is added to every <em>cv<sub class="supsub">k</sub><sup class="supsub">3</sup></em> for <em>0 < k < i</em>.</li>
</ul>
</ul>
[ <em>Note</em>: Given similar types <tt>T1</tt> and <tt>T2</tt>, this construction ensures that both can be converted to <tt>T3</tt>. &mdash; <em>end note</em> ] A prvalue expression of type <tt>T</tt><sub>1</sub> can be converted to type <tt>T</tt><sub>2</sub> if <ins>the cv-combined type of <tt>T</tt><sub>1</sub> and <tt>T</tt><sub>2</sub> is <tt>T</tt><sub>2</sub>.</ins> <del>the following conditions are 
	satisfied, where <em>cv<sub class="supsub">i</sub><sup class="supsub">j</sup></em> denotes the cv-qualifiers in the cv-qualification signature of <tt>T</tt><sub><em>j</em></sub>:<br>
(3.1) &mdash; <tt>T</tt><sub>1</sub> and <tt>T</tt><sub>2</sub> are similar.<br>
(3.2) &mdash; For every <em>i</em> > 0, if <tt>const</tt> is in <em>cv<sub class="supsub">i</sub><sup class="supsub">1</sup></em> then <tt>const</tt> is in <em>cv<sub class="supsub">i</sub><sup class="supsub">2</sup></em>, and similarly for <tt>volatile</tt>.<br>
(3.3) &mdash; If the <em>cv<sub class="supsub">i</sub><sup class="supsub">1</sup></em> and <em>cv<sub class="supsub">i</sub><sup class="supsub">2</sup></em> are different, then <tt>const</tt> is in every <em>cv<sub class="supsub">k</sub><sup class="supsub">1</sup></em> for <em>0 &lt; k &lt; i</em>.<br>
</del>		</li></ol>
		</blockquote>
		
<p><i>Drafting note: it may be worth to consider renaming certain terms starting with &ldquo;cv&rdquo;, e.g. cv-combined types are not merely combining cv-qualifiers anymore. Possibly an editorial issue?</i></p>
	
	<p>Modify 7.6.1.10 [expr.const.cast] &para;3:</p>
	<blockquote class="std">For two similar types <tt>T</tt><sub>1</sub> and <tt>T</tt><sub>2</sub>, a prvalue of type <tt>T</tt><sub>1</sub> may be explicitly converted to the type <tt>T</tt><sub>2</sub> using a <tt>const_cast</tt>
	<ins> if, considering the cv-decompositions ([conv.qual]) of both types, all <em>P<sub class="supsub">i</sub><sup class="supsub">1</sup></em> are the same as <em>P<sub class="supsub">i</sub><sup class="supsub">2</sup></em></ins>. </blockquote>

        <!--
	<p>Modify 9.3.3 [dcl.init.ref] &para;4 as follows:</p>
	
	<blockquote class="std"> Given types &ldquo;<em>cv1</em> <tt>T1</tt>&ldquo; and &ldquo;<em>cv2</em> <tt>T2</tt>&ldquo;, &ldquo;<em>cv1</em> <tt>T1</tt>&ldquo;
	is reference-related to &ldquo;<em>cv2</em> <tt>T2</tt>&ldquo; if 
<ul>
<li><tt>T1</tt> is the same type as <tt>T2</tt>, or</li>
<li><tt>T1</tt> is a base class of <tt>T2</tt><ins>, or </li>
<li><ins>for some type <tt>U</tt>, <tt>T1</tt> is an array type of unknown bound of <tt>U</tt> and <tt>T2</tt> is an
	array type of known bound of <tt>U</tt></ins>.</li>
</ul>

	 </blockquote>
        -->
	
	<p>Modify 9.3.4 [dcl.init.list] &para;(3.8) as follows:</p>
	
	<blockquote class="std">
		Otherwise, if <tt>T</tt> is a reference type, a prvalue <del>of the type referenced by <tt>T</tt></del> is generated. 
		The prvalue initializes its result object by copy-list-initialization or direct-list-initialization, depending on the kind
of initialization for the reference. The prvalue is then used to direct-initialize the reference. <ins>The type of the temporary is the type referenced by <tt>T</tt>, unless <tt>T</tt> is &ldquo;reference to array of unknown bound of <tt>U</tt>&rdquo;, in 
which case the type of the temporary is the type of <tt>x</tt> in the declaration <tt>U x[]</tt> <em>H</em>, where <em>H</em> is the initializer list.</ins>
</blockquote>

	<p>Modify 12.3.3.1.5 [over.ics.list] &para;6 as follows:</p>
	<blockquote class="std">
	Otherwise, if the parameter type is &ldquo;array of <tt>N X</tt>&rdquo; <ins>or &ldquo;array of unknown bound of <tt>X</tt>&rdquo;</ins>, if there exists an implicit conversion sequence <del>for each
          element of the array from the corresponding</del> <ins>from each</ins> element of the initializer list (<del>or</del><ins>and</ins> from <tt>{}</tt> <ins>in the former case</ins> if <del>there is no such
element</del><ins> <tt>N</tt> exceeds the number of elements in the initializer list</ins>) <ins>to <tt>X</tt></ins>, the implicit conversion sequence is the worst such implicit conversion sequence.
	</blockquote>
	
	<p>Augment 12.3.3.2 [over.ics.rank] &para;(3.1) as indicated:</p>
	<blockquote class="std">
	List-initialization sequence <tt>L1</tt> is a better conversion sequence than list-initialization sequence <tt>L2</tt> if
	<ul>
	<li><tt>L1</tt> converts to <tt>std::initializer_list&lt;X&gt;</tt> for some <tt>X</tt> and <tt>L2</tt> does not, or, if not that,</li>
        <li><del><tt>L1</tt> converts to type &ldquo;array of <tt>N1 T</tt>&rdquo;, <tt>L2</tt> converts to type &ldquo;array of <tt>N2 T</tt>&rdquo;, and <tt>N1</tt> is smaller than <tt>N2</tt>, </del></li>
        <li><ins>
            <tt>L1</tt> and <tt>L2</tt> convert to arrays of the same element type,
            and either
            the number of elements <i>n</i><sub>1</sub> initialized by <tt>L1</tt> is less than the number of elements <i>n</i><sub>2</sub> initialized by <tt>L2</tt>,
            or <i>n</i><sub>1</sub> = <i>n</i><sub>2</sub> and <tt>L2</tt> converts to an array of unknown bound and <tt>L1</tt> does not,</ins>
        </li>

        <!--
	<li><tt>L1</tt> converts to type &ldquo;array of <tt>N1 T</tt>&rdquo;, <tt>L2</tt> converts to type &ldquo;array of <tt>N2 T</tt>&rdquo;, and <tt>N1</tt> is smaller than <tt>N2</tt>, <ins>or if not that,</ins></li>
	<li><ins><tt>L1</tt> converts to type &ldquo;array of unknown bound of <tt>X</tt>&rdquo; and <tt>L2</tt> converts to type &ldquo;array of <tt>N X</tt>&rdquo;, where a variable of the former type, if initialized with the initializer list, would have an array type with a different bound than <tt>N</tt>, or if not that,</ins></li>
	<li><ins><tt>L1</tt> converts to type &ldquo;array of <tt>N X</tt>&rdquo; and <tt>L2</tt> converts to type &ldquo;array of unknown bound of <tt>X</tt>&rdquo;,</ins></li>
        -->
	</ul>
	even if one of the other rules in this paragraph would otherwise apply. <ins> [<em>Example:</em>
	<pre class="example">
void f(int    (&&)[] );    // #1
void f(double (&&)[] );    // #2
void f(int    (&&)[2]);    // #3

f( {1} );          // Calls #1: Better than #2 due to conversion, better than #3 due to bounds
f( {1.0} );        // Calls #2: Identity conversion is better than floating-integral conversion
f( {1.0, 2.0} );   // Calls #2: Identity conversion is better than floating-integral conversion
f( {1, 2} );       // Calls #3: Converting to array of known bound is better than to unknown bound, 
                   //           and an identity conversion is better than floating-integral conversion
</pre>
	<em>&mdash; end example</em>]</ins> [<em>Example:</em>&hellip;<em>&mdash; end example</em>]
	</blockquote>
	
	<p>Modify 12.3.3.2 [over.ics.rank] &para;(3.2.5):</p>
	<blockquote class="std"><tt>S1</tt> and <tt>S2</tt> differ only in their qualification conversion and yield similar types <tt>T1</tt> and <tt>T2</tt> (7.5),
respectively, <del>and the cv-qualification signature of type <tt>T1</tt> is a proper subset of the cv-qualification
signature of type <tt>T2</tt></del><ins>where <tt>T1</tt> can be converted to <tt>T2</tt> by a qualification conversion (7.5).</ins></blockquote>
	
	<h3>Acknowledgments</h3>
	<p>The author would like to thank David Krauss, Johannes Schaub and Richard Smith for their valuable feedback.</p>
	
	<h3>References</h3>
<p>[<a id="EWG118">EWG118</a>] &ldquo;[tiny] Allow conversion from pointer to array of known bound to pointer to array of unknown bound&rdquo;: <a href="http://wg21.link/ewg118">wg21.link/ewg118</a></p>
<p>[<a id="CWG393">CWG393</a>] &ldquo;Pointer to array of unknown bound in template argument list in parameter&rdquo;: <a href="http://wg21.link/cwg393">wg21.link/cwg393</a></p>
<p>[<a id="CWG1307">CWG1307</a>] &ldquo;Overload resolution based on size of array <em>initializer-list</em>&rdquo;: <a href="http://wg21.link/cwg1307">wg21.link/cwg1307</a></p>
</body></html>
