<!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=US-ASCII">

<style type="text/css">

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 { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.4em; padding-right: 0.4em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.4em; padding-right: 0.4em; border: none; }

</style>

<title>Narrowing and Widening Conversions</title>
</head>
<body>

<table style="margin-left: 2em; float:right">
<tbody>
<tr><th>Project:</th><td>ISO JTC1/SC22/WG21:<br>Programming Language C++</td></tr>
<tr><th>Number:</th><td>P1818R1</td></tr>
<tr><th>Date:</th><td>2020-01-12</td></tr>
<tr><th>Audience:</th><td>EWGI</td></tr>
<tr><th>Revises:</th><td>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1818r0.html">P1818R0</a>
</td></tr>
<tr><th>Author:</th><td>Lawrence Crowl</td></tr>
<tr><th>Contact</th><td>Lawrence@Crowl.org</td></tr>
</tbody>
</table>


<h1>Narrowing and Widening Conversions</h1>


<h2>Abstract</h2>

<p>
Generalizing narrowing conversions can admit new primitive numeric types
without further updates to the standard.
Giving priority to select widening conversions in overload resolution
will ease the construction and use of overloaded functions.
</p>


<h2>Contents</h2>

<p>
<a href="#Introduction">Introduction</a><br>
<a href="#Problem">Problem</a><br>
<a href="#Workarounds">Workarounds for Library Users</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Casting">Explicit Argument Casting</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Static">Static Function Declaration</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Local">Local Extern Declaration</a><br>
<a href="#Workarounds">Workarounds for Library Authors</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Overloads">Add Additional Overloads</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Explicit">Make Lossy Conversions Explicit</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Common">Common Type Template</a><br>
<a href="#Solution">Solution</a><br>
<a href="#Proposal">Proposal</a><br>
<a href="#Consequences">Consequences</a><br>
<a href="#Wording">Wording</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#lex.key">5.11 Keywords [lex.key]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#conv">7.3 Standard conversions [conv]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#conv.prom">7.3.6 Integral promotions [conv]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#conv">7.4 Usual arithmetic conversions [expr.arith.conv]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#dcl.init.list">9.3.4 List-initialization [dcl.init.list]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#class.ctor">10.3.4 Constructors [class.ctor]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#over.match.best">11.3.3 Best viable function [over.match.best]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#over.best.ics">11.3.3.1 Implicit conversion sequences [over.best.ics]</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#cpp.predefined">15.11 Predefined macro names [cpp.predefined]</a><br>
<a href="#Revisions">Revision History</a><br>
<a href="#References">References</a><br>
</p>


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

<p>
Overload resolution relies on finding the best implicit conversion sequence
from each argument type to its corresponding parameter type
in all candidate functions.
When no one function has all the best sequences, 
the function call is ambiguous and therefore ill-formed.
</p>

<p>
While many standard conversions may appear in a conversion sequence,
only one user-defined conversion may appear.
</p>

<p>
The standard distinguishes between
information-preserving (widening) conversions and
information-destroying (narrowing) conversions
in two ways.
First, the standard promotions are widening conversions.
Second, initializer-list initialization defines some conversions
as narrowing conversions and permits them
only when the source is constexpr and
the value is within the value set of the destination type.
<p>


<h2><a name="Problem">Problem</a></h2>

<p>
Consider the following declarations.
</p>

<pre class=example>
<code>float atan2( float, float );
double atan2( double, double );
long double atan2( long double, long double );</code>
</pre>

<p>
Of the nine possible call argument-type combinations (3<sup>2</sup>),
six are ambiguous.
However, all combinations have a best overload,
which preserves all the information in the arguments
and operates at the least cost.
</p>

<pre class=example><code>int main() {
  float f; double d; long double ld;
  atan2(  f,  f ); // matches float
  atan2(  f,  d ); // ambiguous, want double
  atan2(  f, ld ); // ambiguous, want long double
  atan2(  d,  f ); // ambiguous, want double
  atan2(  d,  d ); // matches double
  atan2(  d, ld ); // ambiguous, want long double
  atan2( ld,  f ); // ambiguous, want long double
  atan2( ld,  d ); // ambiguous, want long double
  atan2( ld, ld ); // matches long double
}</code>
</pre>

<p>
The problem extends to user-defined types and functions as well.
</p>

<pre class=example>
class cardinal {
  unsigned int c;
public:
  cardinal();
};

class integral {
  int c;
public:
  integral();
  integral( cardinal );
  operator cardinal();
};

class rational {
  integral n, d;
public:
  rational();
  rational( cardinal );
  operator cardinal();
  rational( integral );
  operator integral();
};

cardinal operator+( cardinal, cardinal );
integral operator+( integral, integral );
rational operator+( rational, rational );

int main() {
  cardinal c; integral i; rational r;
  c + c; // matches cardinal
  c + i; // ambiguous, want integral
  c + r; // ambiguous, want rational
  i + c; // ambiguous, want integral
  i + i; // matches integral
  i + r; // ambiguous, want rational
  r + c; // ambiguous, want rational
  r + i; // ambiguous, want rational
  r + r; // matches rational
}
</pre>

<p>
A related problem is that
adding a new overload into a header
may introduce an ambiguity in client code.
Such problems may not be found until well after products have shipped.
</p>

<p>
C++ programs will be easier to write and more robust to environmental changes
if the language provides a better mechanism for finding the 'best' overload.
</p>


<h2><a name="Workarounds">Workarounds for Library Users</a></h2>

<p>
The user of library must necessarily be able to work around any ambiguity.
</p>


<h3><a name="Casting">Explicit Argument Casting</a></h3>

<p>
The typical workaround to ambiguity for library users
is to add explicit casts to the call sites.
</p>

<pre class=example>
<code>int main() {
  atan2( f, f );
  atan2( static_cast&lt;double&gt;(f), d );
  atan2( static_cast&lt;long double&gt;(f), ld );
  atan2( d, static_cast&lt;double&gt;(f) );
  atan2( d, d );
  atan2( static_cast&lt;long double&gt;(d), ld );
  atan2( ld, static_cast&lt;long double&gt;(f) );
  atan2( ld, static_cast&lt;long double&gt;(d) );
  atan2( ld, ld );
}</code>
</pre>

<p>
Unfortunately, this approach now binds one particular overload to the call.
A more appropriate overload added later will not be found.
</p>


<h3><a name="Static">Static Function Declaration</a></h3>

<p>
One workaround to this problem is to define a local static function
with exactly the needed arguments..
</p>

<pre class=example>
<code>static long double atan2( float f, double d )
  return atan2( static_cast&lt;double&gt;(f), d );
}

int main() {
  float f; double d;
  atan2( f, d );
}</code>
</pre>

<p>
Unfortunately, as above,
this approach now binds one particular overload to the call.
A more appropriate overload added later will not be found.
</p>


<h3><a name="Local">Local Extern Declaration</a></h3>

<p>
One workaround to this problem is Daveed Vandervorde's technique
that adds a local extern function declaration to force a particular overload.
</p>

<pre class=example>
<code>int main() {
  float f; double d;
  extern long double atan2( double, double );
  return atan2( f, d );
}</code>
</pre>

<p>
This technique is effective, but not well known.
It lacks generality in that it does not apply to member functions.
More importantly, as more overloads are used within the function,
the ambiguity problem resurfaces.
Finally, as above,
this approach now binds one particular overload to the call.
A more appropriate overload added later will not be found.
</p>


<h2><a name="Workarounds">Workarounds for Library Authors</a></h2>

<p>
Library authors can anticipate some problems.
</p>


<h3><a name="Overloads">Add Additional Overloads</a></h3>

<p>
The primary workaround is to add more overloaded functions.
</p>

<pre class=example>
double atan2( float f, double d )
  { return atan2( static_cast&lt;double&gt;(c), i ); }
long double atan2( float f, long double ld )
  { return atan2( static_cast&lt;long double&gt;(c), r ); }
double atan2( double d, float f )
  { return atan2( i, static_cast&lt;double&gt;(c) ); }
long double atan2( double d, long double ld )
  { return atan2( static_cast&lt;long double&gt;(i), r ); }
long double atan2( long double ld, float f )
  { return atan2( r, static_cast&lt;long double&gt;(c) ); }
long double atan2( long double ld, double d )
  { return atan2( r, static_cast&lt;long double&gt;(i) ); }
</pre>

<p>
Unfortunately, number of additional overloads needed
grows dramatically with increasing number of types and parameters.
This growth places a specification burden on the library author.
It also places a burden on the library user,
because the number of overloads that must be excluded by a call also grows.
</p>

<p>
More problematically, independently developed types with the same functions
will not have the additional overloads
because the authors could not have anticipated the need.
</p>


<h3><a name="Explicit">Make Lossy Conversions Explicit</a></h3>

<p>
Problematic conversion can be excluded from overloading
by making them explicit.
</p>

<pre class=example>
explicit integral( rational );
</pre>

<p>
Unfortunately, this approach requires users to cast arguments
even when there would otherwise be no ambiguity.
Furthermore, with this approach, the argument is bound to one type.
A better function overload introduced later will not be selected
because the type of the argument has been frozen into the cast.
</p>

<p>
A computed explicit, i.e. <code>explicit(<var>expr</var>)</code>, does not work
because the function definition does not know the context of the call.
That is, the function definition cannot anticipate competing widening overloads.
</p>


<h3><a name="Common">Common Type Template</a></h3>

<p>
Another approach to solving the problem is to write templates
that convert arguments to a common type (e.g. <a href="#P0880R2">[P0880R2]</a>).
</p>

<p>
Unfortunately, this approach has difficulty
with argument-dependent lookup and namespaces.
</p>


<h2><a name="Solution">Solution</a></h2>

<p>
In the examples above,
there is always one least-common information-preserving overload.
There are two mechanisms that make identifying this overload possible.
First, we prefer widening conversions over narrowing conversions.
Second, we prefer the widening conversion that covers the least distance.
</p>

<p>
In <a href="#N3387">[N3387]</a>,
Jens Maurer applied these principles to the built-in integer types
by adjusting the rules for integer conversion rank and promotion.
The paper was not persued.
We intend to generalize the approach to user-defined types.
</p>


<h2><a name="Proposal">Proposal</a></h2>

<p>
We propose to introduce a distinction
between narrowing and widening conversions in C++ programs.
We then propose to alter overload resolution rules
to prefer widening conversion over narrowing conversions.
</p>

<p>
For built-in types:
<p>
<ul>
<li>Generalize the definition of conversion to value sets
rather than specific conversion type pairs.</li>
<li>Define widening standard conversions as
those from a lower rank to those those with a higher rank
where the value set of the former is a subset of the later.</li>
<li>Modify overload rules to prefer widening standard conversions
over other standard conversions.</li>
</ul>

<p>
For user-defined types:
</p>
<ul>

<li>Add a keyword <code>widening</code>,
to be used where <code>explicit</code> may appear,
which declares a user-defined conversion to be a widening conversion.</li>

<li>Define a widening standard conversion sequence to be
a standard conversion sequence
that does not have narrowing standard conversions.</li>

<li>Define a widening user-defined conversion sequence to be
a widening standard conversion sequence,
a widening user-defined conversion, and
a widening standard conversion sequence.</li>

<li>Change function/operator overloading to prefer either:
<ul>
<li>option 1: standard conversion sequence
over widening user-defined conversion sequence
over non-widening user-defined conversion sequence; or</li>
<li>option 2: widening standard conversion sequence
over widening user-defined conversion sequence
over narrowing user-defined conversion sequence
over non-widening user-defined conversion sequence</li>
</ul>
</li>

<li>Change function/operator overloading
to prefer the 'nearest' widening conversion.
The nearest widening conversion is the one
that preserves more possible subsequent conversions.
Specifically, given widening conversions
A&rarr;B, A&rarr;C and B&rarr;C,
but not C&rarr;B, 
a conversion from A to B preserves a subsequent B to C.
On the other hand, a conversion from A to C has no subsequent conversions.</li>

<li>Make <code>widening</code> compile-time computable,
i.e. <code>widening(<var>boolean_expression</var>)</code>.</li>

<li>To make the above achievable,
provide a <code>widening</code> trait.
Notionally, this would be something like
<code>std::is_widening_convertable&lt; <var>T</var>, <var>R</var> &gt;</code>
</li>

</ul>


<h2><a name="Consequences">Consequences</a></h2>

<p>
The proposal only removes ambiguity, it does not introduce it.
So, all existing correct code is unchanged.
</p>

<p>
The two's complement representation for built-in integer types
implies signed&rarr;unsigned conversions are not widening.
Likewise, unsigned&rarr;signed conversions are not widening.
</p>

<p>
When programmers change existing user-defined conversions
to widening conversions,
option 1 will not introduce user-defined conversions where none existed before.
It is unclear whether option 2 may do so.
</p>

<p>
The parameter types have widening conversions,
adding new overloads reduces the chance that
existing calls will become ambiguous.
</p>

<p>
In the case where an existing call would be ambiguous,
overload resolution would become more expensive.
This expense must be evaluated with respect to the workarounds.
</p>


<h2><a name="Wording">Wording</a></h2>

<p>
All wording edits are relative to
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4800.pdf">
N4800 Working Draft, Standard for Programming Language C++</a>.
</p>

<p>
THE WORDING IS NOT YET COMPLETE.
</p>


<h3><a name="lex.key">5.11 Keywords [lex.key]</a></h3>

<p>
Add <code>widening</code> to Table 5 &mdash; Keywords.
</p>


<h3><a name="conv">7.3 Standard conversions [conv]</a></h3>


<h3><a name="conv.prom">7.3.6 Integral promotions [conv]</a></h3>

<p>
Paragraph 2 defines promotion of unsigned short to int,
which loses the invariant that the value is not negative.
</p>


<h3><a name="conv">7.4 Usual arithmetic conversions [expr.arith.conv]</a></h3>

<p>
Conversion from a signed integer type to an unsigned integer type
may change the numeric value.
Better would be to convert to the smallest signed type
that contains all the values of both of the arguments.
</p>

<p>
Bullet (1.5.3) says:
</p>

<blockquote class="std">
<p>
Otherwise, if the operand that has unsigned integer type
has rank greater than or equal to the rank of the type of the other operand,
the operand with signed integer type
shall be converted to the type of the operand with unsigned integer type. 
</p>
</blockquote>

<p>
Bullet (1.5.4) says:
</p>

<blockquote class="std">
<p>
Otherwise, if the type of the operand with signed integer type
can represent all of the values of the type
of the operand with unsigned integer type,
the operand with unsigned integer type
shall be converted to the type of the operand with signed integer type.
</p>
</blockquote>

<p>
Bullet (1.5.5) says:
</p>

<blockquote class="std">
<p>
Otherwise, both operands
shall be converted to the unsigned integer type
corresponding to the type of the operand with signed integer type.
</p>
</blockquote>


<h3><a name="dcl.init.list">9.3.4 List-initialization [dcl.init.list]</a></h3>

<p>
Paragraph 7 defines narrowing conversion.
</p>


<h3><a name="class.ctor">10.3.4 Constructors [class.ctor]</a></h3>


<h3><a name="over.match.best">11.3.3 Best viable function [over.match.best]</a></h3>


<h3><a name="over.best.ics">11.3.3.1 Implicit conversion sequences [over.best.ics]</a></h3>


<h3><a name="cpp.predefined">15.11 Predefined macro names [cpp.predefined]</a></h3>

<p>
Add predefined feature test macros to table 17.
</p>


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

<p>
This paper revises
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1818r0.html">
P1818R0</a>.
</p>

<ul>
<li>Clarify the problem.</li>
<li>Clarify the concerns with workarounds.</li>
<li>Add more examples.</li>
<li>Add <code>bool</code> parameter to <code>widening</code>.</li>
<li>Provide more feedback on consequences.</li>
<li>General editorial changes.</li>
</ul>


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

<dl>

<dt><a name=N3387>[N3387]</a></dt>
<dd>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3387.html">
N3387</a> Overload resolution tiebreakers for integer types;
2012-09-12;
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3387.html">
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3387.html</a>
</dd>

<dt><a name=P0880R2>[P0880R2]</a></dt>
<dd>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0880r2.html">
N3387</a> Numbers interaction;
2019-01-15;
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0880r2.html">
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0880r2.html</a>
</dd>

<dt><a name=N4800>[N4800]</a></dt>
<dd>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4800.pdf">
N4800</a>
Working Draft, Standard for Programming Language C++;
2019-01-21;
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4800.pdf">
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/n4800.pdf</a>
</dd>

<dt><a name=P0192R4>[P0192R4]</a></dt>
<dd>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0192r4.html">
P0192R4</a>
`short float` and fixed-size floating point types;
2018-10-08;
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0192r4.html">
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0192r4.html</a>
</dd>

<dt><a name=P1467R0>[P1467R0]</a></dt>
<dd>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1467r0.html">
P0192R4</a>
Extended floating-point types;
2019-01-21;
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1467r0.html">
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1467r0.html</a>
</dd>

</dl>


</body></html>
