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

</style>

<title>A proposal for a type trait to detect narrowing conversions</title>
</head>
<body>
<h1>A proposal for a type trait to detect narrowing conversions</h1>
<p>ISO/IEC JTC1 SC22 WG21 P0870R1 2017-11-21</p>
<p>Project: Programming Language C++</p>
<p>Audience: Library Evolution Working Group Incubator</p>
<address>Giuseppe D'Angelo, giuseppe.dangelo@kdab.com</address>


<h2>Introduction</h2>

<p>This paper proposes a new type trait for the C++ Standard Library,
<code>is_narrowing_convertible</code>, to detect whether a type is
implictly convertible to another type by going through a narrowing
conversion.</p>

<h2>Changelog</h2>

<ul>
<li>P0870R1: submitted for the Prague mailing, targeting LEWGI.</li>
</ul>


<h2>Motivation and Scope</h2>

<p>A <em>narrowing conversion</em> is formally defined by the C++ Standard
in [dcl.init.list], with the intent of forbidding them from
list-initializations.</p> 

<p>It is useful in certain contexts to know whether a conversion between two types
would qualify as a narrowing conversion. For instance, it may be useful to
inhibit (via SFINAE) construction or conversion of "wrapper" types
(like <code>std::variant</code> or <code>std::optional</code>) when
a narrowing conversion is requested.</p>

<p>A use case the author has recently worked on was to prohibit narrowing
conversions when establishing a connection in the Qt framework 
(see [<a href="#QT_NO_NARROWING_CONVERSIONS_IN_CONNECT">Qt</a>]),
with the intent of making the type system more robust. Simplifying,
a connection is established between a <em>signal</em> non-static member function
of an object and a <em>slot</em> non-static member function of another object. 
The signal's and the slot's argument lists must have compatible arguments
for the connection to be established: the signal must have at least as
many arguments as the slot, and the each argument of the signal must be
implictly convertible to the corresponding argument of the slot. In case
of a mismatch, a compile-time error is generated. Bugs have been
observed when connections were successfully established, but a narrowing
conversion (and subsequent loss of data/precision) was happening. Detecting such
narrowing conversions, and making the connection fail for such cases,
would have prevented the bugs.</p>


<h2>Impact On The Standard</h2>

<p>This proposal is a pure library extension.</p>

<p>It proposes changes to an existing header, <code>&lt;type_traits&gt;</code>,
but it does not require changes to any standard classes or functions and
it does not require changes to any of the standard requirement tables.</p>

<p>This proposal does not require any changes in the core language,
and it has been implemented in standard C++.</p>

<p>This proposal does not depend on any other library extensions.</p>

<p>This proposal affects [<a href="#P0608R0">P0608R0</a>], a proposed resolution
for [<a href="LEWG227">LEWG 227</a>]. The type trait described by this proposal
has similar semantics to the exposition only <code>is_non_narrowing_convertible_v</code>
trait defined by [<a href="#P0608R0">P0608R0</a>]. The semantics described by
[<a href="#P0608R0">P0608R0</a>] are however different from the ones defined
by this proposal: in particular, the present proposal does not address boolean
conversions [conv.bool], and the present proposal uses a positive tense
instead of a negative one.</p>


<h2>Design Decisions</h2>

<p>The most natural place to add the trait presented by this proposal
is the already existing <code>&lt;type_traits&gt;</code> header, which
collects most (if not all) of the type traits available from the Standard Library.</p>

<p>In the <code>&lt;type_traits&gt;</code> header the <code>is_convertible</code>
type trait checks whether a type is implictly convertible to another type.
Building upon this established name, the proposed name for the trait 
described by this proposal shall therefore be <code>is_narrowing_convertible</code>,
with the corresponding <code>is_narrowing_convertible_v</code> type trait helper
variable template.</p>

<h3>Should <code>is_convertible_v&lt;From, To&gt; == true</code> be
a precondition of trying to instantiate 
<code>is_narrowing_convertible&lt;From, To&gt;</code>?</h3>

<p>The author deems this unnecessary. Adding such a precondition would
likely make the usage of the proposed trait more difficult and/or error prone.
<p>First and foremost, it's not going to simplify the specification for the proposed
trait, or its implementation (as far as the author can see).
Second, the current wording for [dcl.init.list] restricts narrowing conversion
to a fixed number of cases: between certain integer and floating point types,
as well as from unscoped enumeration types to a integer or a floating point type.
Any other conversion is never a narrowing conversion; therefore, there is
no need of requiring <code>is_convertible_v</code> to be <code>true</code>.</p>
<p><code>is_convertible</code>'s own prerequisites are another discussion point
(they would become <code>is_narrowing_convertible</code>'s prerequisites).
In [meta.rel], the <em>Type relationship predicates</em> table
says that the prerequisites for <code>is_convertible&lt;From, To&gt;</code> are:</p>

<blockquote class="std">
<p>
<code>From</code> and <code>To</code> shall be complete types, arrays of unknown bound, or <em>cv</em> void types.
</p>
</blockquote>

<p>These prerequisites are not necessary for <code>is_narrowing_convertible</code>:
for instance, none of the types that can be involved in a narrowing conversions
can possibly be incomplete.</p>

<h3>Should <code>is_narrowing_convertible_v&lt;From, To&gt;</code> yield
<code>false</code> for boolean conversions [conv.bool]?</h3>

<p>The author deems this to be out of scope for the proposed trait, which should have
only the semantics defined by the language regarding narrowing conversion.
Another trait should be therefore added to detect (implicit) boolean conversions, which,
according to the current wording of [dcl.init.list], are never considered narrowing conversions.</p>
<p>It is the author's opinion that, in general, implicit boolean conversions are undesiderable
for the same reasons that make narrowing conversions unwanted in certain contexts
&mdash; in the sense that a conversion towards bool may discard
important information. However, we recognize the importance of not imbuing a type trait
with extra, not yet well-defined semantics, and therefore we are excluding
boolean conversions from the present proposal.</p>

<h3>Bikeshedding: <code>is_narrowing_convertible</code> or <code>is_non_narrowing_convertible</code>?</h3>

<p>The Standard Library does not currently have type traits with a negation in their names,
so the current proposal uses a positive tense, although we foresee that the majority
of the usages are going to be about detecting whether there is <em>no</em>
narrowing conversion between two given types. Note that Standard Library
names such as <code>is_nothrow_constructible</code> are not considered
to be negations; the tense is positive, using <code>nothrow</code> as
an adjective/attribute.</p>

<h3>Bikeshedding: <code>is_narrowing_convertible</code> or <code>is_narrowing</code>?</h3>

<p>This paper proposes the former, building on top of the existing <code>is_convertible</code> name;
however, we concede that <code>is_narrowing</code> could lead to cleaner code.
Moreover, at the present time, there should be no ambiguity
over the meaning of <code>is_narrowing</code>: the only usage of "narrowing"
in the Standard is about narrowing conversions.
Nonetheless, it may make sense to future-proof the name proposed here, and
therefore go for the slightly more verbose <code>is_narrowing_convertible</code>.</p>

<h2>Technical Specifications</h2>

<p>The proposed type trait has already been implemented in various codebases using
ad-hoc solutions (depending on the quality of the compiler, etc.). The most
straighforward implementations rely on SFINAE using an expression
such as <code>To{std::declval&lt;From&gt;()}</code>.</p>

<h3>Standardese</h3>

<p>In [meta.type.synop], add to the the header synopsis:</p>

<blockquote class="std">
<pre>
<code>
  template &lt;class From, class To&gt; struct is_convertible;
<ins>  template &lt;class From, class To&gt; struct is_narrowing_convertible;</ins>
</code>
</pre>
</blockquote>

<blockquote class="std">
<pre>
<code>
  template &lt;class From, class To&gt;
    inline constexpr bool is_convertible_v = is_convertible&lt;From, To&gt;::value;
<ins>  template &lt;class From, class To&gt;
    inline constexpr bool is_narrowing_convertible_v = is_narrowing_convertible&lt;From, To&gt;::value;</ins>
</code>
</pre>
</blockquote>

<p>In [meta.rel], add a new row to the <em>Type relationship predicates</em> table,
after the entry for <code>is_convertible</code>:</p>

<blockquote class="std">
<dl class="attribute">
<dt>Template</dt>
<dd><p>
<ins><code>template &lt;class From, class To&gt; struct is_narrowing_convertible;</code></ins>
</p></dd>

<dt>Condition</dt>
<dd><p>
<ins><em>see below</em></ins>
</p></dd>

<dt>Comments</dt>
<dd><p><ins>&mdash;</ins></p></dd>
</dl>
</blockquote>

<p>Modify paragraph 5 of [meta.rel]:</p>

<blockquote class="std">
<p>5. The predicate condition for a template specialization
<code>is_convertible&lt;From, To&gt;</code><ins> or a template specialization
<code>is_narrowing_convertible&lt;From, To&gt;</code></ins> shall be satisfied
if and only if the return expression in the following code would be well-formed,
including any implicit conversions to the return type of the function<ins>;
for <code>is_narrowing_convertible&lt;From, To&gt;</code> the conversion shall
moreover be a narrowing conversion ([dcl.init.list])</ins>:</p>
<pre>
<code>
To test() {
  return declval&lt;From&gt;();
}
</code>
</pre>
<p>[ <i>Note:</i> This requirement gives well-defined results for reference
types, void types, array types, and function types. <i>&mdash; end note</i> ]
<ins>[ <i>Note:</i> For the purpose of establishing whether the conversion is a
narrowing conversion, the source is never a constant expression.
<i>&mdash; end note</i> ]</ins></p>
</blockquote>

<h2>Acknowledgements</h2>

<p>Thanks to KDAB for supporting this work.</p>
<p>Thanks to Marc Mutz, Andr&eacute; Somers and Zhihao Yuan for their feedback.</p>

<h2>References</h2>

<p>[<a name="QT_NO_NARROWING_CONVERSIONS_IN_CONNECT">Qt</a>] Qt version 5.9, <i><a href="https://doc.qt.io/qt-5/qobject.html#QT_NO_NARROWING_CONVERSIONS_IN_CONNECT">QT_NO_NARROWING_CONVERSIONS_IN_CONNECT</a></i>, QObject class documentation</p>
<p>[<a name="P0608R0">P0608R0</a>] Zhihao Yuan, <i><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0608r0.html">A sane variant converting constructor (LEWG 227)</a></i></p>
<p>[<a name="LEWG227">LEWG 227</a>] Zhihao Yuan, <i><a href="https://issues.isocpp.org/show_bug.cgi?id=227">Bug 227 - variant converting constructor allows unintended conversions </a></i></p>

</body>
