<!DOCTYPE html>
<html>
<head>
<meta name="generator" content=
"HTML Tidy for HTML5 for Linux version 5.6.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>P0340R1: Making std::underlying_type SFINAE-friendly</title>

<style type="text/css">
  p {text-align:justify}
  li {text-align:justify}
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins { text-decoration:none; font-weight:bold; background-color:#A0FFA0 }
  del { text-decoration:line-through; background-color:#FFA0A0 }
  #enumonly:checked ~ * .wchar { display:none; visibility:hidden }
  #enumonly:checked ~ * .char { display:none; visibility:hidden }
  #usewidechar:checked ~ * .char { display:none; visibility:hidden }
  #haspassthrough:checked ~ * .notype { display:none; visibility:hidden }
  #haspassthrough:not(:checked) ~ * .passthrough { display:none; visibility:hidden }
</style>
</head>
<body>
<h1 id="making-std-underlying_type-sfinae-friendly">Making
<code>std::underlying_type</code> SFINAE-friendly</h1>
<table>
<tbody>
<tr>
<td>Document number:</td>
<td>P0340R1</td>
</tr>
<tr>
<td>Date:</td>
<td>2018-05-07</td>
</tr>
<tr>
<td>Audience:</td>
<td>LEWG &rarr; LWG</td>
</tr>
<tr>
<td>Reply to:</td>
<td>R. "Tim" Song &lt;rs2740@gmail.com&gt;</td>
</tr>
</tbody>
</table>
<h2 id="revision-history">Revision history</h2>
<ul>
<li>R1 (2018-05-07): Updated wording to current WP and added alternative wording options. </li>
</ul>
<h2 id="abstract">Abstract</h2>
<p>This paper proposes making <code>std::underlying_type</code>
SFINAE-friendly. In particular, it would make instantiating
<code>std::underlying_type&lt;T&gt;</code> for a non-enumeration
type <code>T</code> well-defined and result in an empty struct.</p>
<h2 id="motivation">Motivation</h2>
<p>Currently, <code>std::underlying_type&lt;T&gt;</code> requires
<code>T</code> to be a complete enumeration type. instantiating it
with any other type results in undefined behavior, typically a hard
error. This makes it tricky to use in SFINAE contexts. For example,
if one wants to write a function template that want to constrain a
template argument to "enumeration type whose underlying type is
<code>int</code>", the "obvious" approach would be something along
the lines of</p>
<pre><code>template&lt;class T&gt;
std::enable_if_t&lt;std::is_enum_v&lt;T&gt; &amp;&
                 std::is_same_v&lt;std::underlying_type_t&lt;T&gt;, int&gt;&gt; foo(T t);
</code></pre>
<p>Unfortunately, this won't work; writing <code>foo(0)</code> will
almost certainly result in a hard error, even if there is a
<code>void foo(int);</code> overload available. Instead, actual
evaluation of <code>std::underlying_type&lt;T&gt;</code> must be
deferred until <code>T</code> is known to be an enum, with
something like</p>
<pre><code>template&lt;class T&gt;
std::enable_if_t&lt;std::is_same_v&lt;typename std::enable_if_t&lt;std::is_enum_v&lt;T&gt;, std::underlying_type&lt;T&gt;&gt;::type, int&gt;&gt; foo(T t);
</code></pre>
<p>Not only is this harder to write (<code>typename
...::type</code>), it is also harder to understand (nested
<code>enable_if</code>s).</p>
<p>This is a recurring problem on StackOverflow; see for example
<a href="http://stackoverflow.com/q/36568050/2756719">1</a>,
<a href="http://stackoverflow.com/q/35746062/2756719">2</a>,
<a href="http://stackoverflow.com/q/35152818/2756719">3</a>, all
from 2016.</p>
<p>If <code>std::underlying_type&lt;T&gt;</code> is well-defined
but has no member <code>type</code> when <code>T</code> is not an
enumeration type, then the above function template can be
simplified to</p>
<pre><code>template&lt;class T&gt;
std::enable_if_t&lt;std::is_same_v&lt;std::underlying_type_t&lt;T&gt;, int&gt;&gt; foo(T t);
</code></pre>
<p>which is shorter, more intuitive, and easier to understand.</p>
<p>It's worth noting that libc++ has a
<code>__sfinae_underlying_type</code> for internal use with an
implementation very similar to that described below (but with an
additional helper member typedef).</p>
<h2 id="impact-on-the-standard">Impact on the standard</h2>
<p>This is a pure extension, as currently instantiating
<code>std::underlying_type</code> over a non-enumeration type
results in undefined behavior, typically a compile-time error.</p>
<h2 id="design-alternatives">Design alternatives</h2>
<p>A possible alternative would be to make the nested typedef
<code>type</code> return the type unchanged if <code>T</code> is
not an enumeration type, matching the behavior of some other
TransformationTraits such as <code>add_lvalue_reference</code> and
<code>remove_extent</code>. This approach also avoids hard errors.
This author, however, does not see reasons to make
<code>underlying_type_t&lt;int&gt;</code> well-formed.</p>
<p>Another design alternative would be to support
<code>char16_t</code>, <code>char32_t</code>, and
<code>wchar_t</code>, each of which also has a "underlying type"
(see [basic.fundamental]/5). And though the standard doesn't use
the term, one might say that plain <code>char</code> similarly has
an "underlying type" of either <code>signed char</code> or
<code>unsigned char</code> depending on the implementation. The
current specification in the standard permits implementations to
support those types as an extension, though the author is not aware
of any implementation that does so. In the author's opinion, the
two categories are sufficiently distinct that lumping them into the
same type trait would not be advisable. </p>
<p>The prohibition against incomplete enumeration types is left
undisturbed, given the potential for ODR violations, and because the
benefit from supporting such types is minimal at best.</p>
<h2 id="proposed-wording">Proposed wording</h2>
<p>To facilitate discussion, wording is provided for all three possible variations of supported types (enumeration only, types with a core language "underlying type" only, and types with a core language "underlying type" plus <code>char</code>) and both possible handling of unsupported types (no member <code>type</code> and pass-through).</p>
<input type="radio" name="alternative" id="enumonly" checked=""/><label for="enumonly">Support enumeration types only</label><br/>
<input type="radio" name="alternative" id="usewidechar"/><label for="usewidechar">Support wide character types (<code>wchar_t</code>, <code>char16_t</code>, <code>char32_t</code>) and enumeration types</label><br/>
<input type="radio" name="alternative" id="usechar"/><label for="usechar">Support <code>char</code>, wide character types, and enumeration types</label><br/>
<input type="checkbox" id="haspassthrough"/><label for="haspassthrough">Pass through other types (rather than having no <code>type</code>)</label>
<p>Edit the table in [meta.trans.other] as indicated:</p>
<table border="1">
<thead>
<tr>
<th>Template</th>
<th>Comments</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td><code>template &lt;class T&gt;</code><br>
<code>struct underlying_type;</code></td>
<td>
<p><ins>If <code>T</code> is an enumeration type</ins><ins class="wchar">, <i>cv</i> <code>wchar_t</code>, <i>cv</i> <code>char16_t</code>, or <i>cv</i> <code>char32_t</code></ins><ins>, the</ins>
<del>The</del> member typedef <code>type</code> shall name the
underlying type of <code>T</code><ins> (10.2 [dcl.enum]</ins><ins class="wchar">, 6.7.1 [basic.fundamental]</ins><ins>)</ins><ins class="char">; otherwise, if <code>T</code> is <i>cv</i> <code>char</code>, the member typedef <code>type</code> shall name the type <code>unsigned char</code> if <code>is_unsigned_v&lt;char&gt;</code> is <code>true</code> and <code>signed char</code> otherwise</ins><ins class="notype">; otherwise, there shall be
no member <code>type</code></ins><ins class="passthrough">; otherwise, the member typedef <code>type</code> shall name <code>T</code></ins>.</p>
<p><i>Requires:</i> <del><code>T</code> shall be a complete enumeration type
(10.2)</del> <ins>If <code>T</code> is an enumeration type,
<code>T</code> shall be a complete type.</ins></p>
</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<h2 id="possible-implementation">Possible implementation</h2>
<p>Given an intrinsic <code>__underlying_type(T)</code> (which is
already needed to implement the current version of
<code>underlying_type</code>), the implementation of the enumeration-only version is trivial:</p>
<pre>
<code> template&lt;class T, bool = std::is_enum_v&lt;T&gt;&gt; struct _Underlying_type {};
 template&lt;class T&gt; struct _Underlying_type&lt;T, true&gt; { using type = __underlying_type(T); };

 template&lt;class T&gt; struct underlying_type : _Underlying_type&lt;T&gt; { };
</code></pre>
<p>Supporting character types is simply a matter of adding a few specializations, and supporting pass-through behavior simply requires adding a <code>using type = T;</code> to the primary <code>_Underlying_type</code> template.</p>
</body>
</html>
