<html>
<head>
  <title>Loosen restrictions on "_t" typedefs. </title>
  <meta Author="Jorg Brown" content="proposal">
</head>

<body bgcolor="#FFFFFF" text="#000000">

<style>
del { background-color:LightPink; }
ins { background-color:LightGreen; }
</style>

<font size=-1>
Jorg Brown &lt;jorg.brown@gmail.com&gt;
<br>
2023-02-06
<br>
Document P1715R1
<code>$Id: proposal.html,v 1.50 2023/02/06 11:50:21 jorg Exp $</code>
</font>

<h1>P1715R1
 (Revision 1)</h1>

<h2>Revision history</h2>

<ul>
  <li>2023-02-06: R1: Proposal to loosen restrictions on "_t" typedefs.</li>
    <ul>
      <li>Don't remove restrictions on those typedefs that could be implemented directly, for example std::add_const_t, since a direct implementation would allow for type deduction to occur and thus remove the need to explicitly specify a template type parameter.</li>
      <li>Update section numbers for c++23.</li>
    </ul>
  <li>2019-06-17: R0: Proposal to loosen restrictions on "_t" typedefs.</li>
</ul>

<h2>Introduction.</h2>

<p>
In C++14, templated typedefs and values were added alongside similarly-named templated classes.  For example:
</p>

<blockquote>
<pre>
// C++11:
// "If B is true, the member typedef type shall equal T. If B is false, the member typedef type shall equal F."
template&lt;bool b, class T, class F>
struct conditional;

// C++14
template&lt;bool b, class T, class F>
using conditional_t = typename conditional&lt;b, T, F>::type;
</pre>
</blockquote>

<p>
Unfortunately, this is all the standard has to say about conditional_t.  In particular, the standard does not leave implementors the option of defining conditional in terms of conditional_t.
</p>

<h2>Motivation and Scope.</h2>

<p>
This definition of conditional_t requires that the compiler instantiate the templated class conditional for each combination of b/T/F template parameters.  This has measurable impact on both compile time, and size of debug information, especially in environments where template meta-programming is in heavy use.
</p>

<p>
Consider an alternative implementation of conditional_t:
</p>

<blockquote>
<pre>
template&lt;bool _Bp> struct __select;
template&lt;> struct __select&lt;true>  { template&lt;typename _TrueT, typename _FalseT> using type = _TrueT;};
template&lt;> struct __select&lt;false> { template&lt;typename _TrueT, typename _FalseT> using type = _FalseT;};

template &lt;bool _Bp, class _TrueT, class _FalseT> using conditional_t = typename __select&lt;_Bp>::template type&lt;_TrueT, _FalseT>;
</pre>
</blockquote>

<p>
In this implementation, no matter how many times conditional_t is used, there are only two types that are instantiated as a result: __select&lt;true> and __select&lt;false>.
</p>

<p>
This not only saves in compiler memory, but also debug information.  Here at Google, for a particularly TMP-heavy file, it turned out that around 1/6th of all classes emitted as part of clang's debug information were instantiations of std::conditional.
</p>

<p>While proposing a change to fix this, and thus reduce debug size, it was pointed out that this change is actually observable.  Suppose a function is declared in a header like this:

<blockquote>
<pre>
template&lt;bool B> long to_long(conditional_t&lt;B, int, long> param);
</pre>
</blockquote>

and then later in the same file, implemented like this, presumably because the programmer accidentally updated only one of two mentions of to_long:

<blockquote>
<pre>
template&lt;bool B> long to_long(typename conditional&lt;B, int, long>::type param) {
  return param;
}
</pre>
</blockquote>
</p>

<p>
The header compiles just fine but if conditional_t has not been defined in terms of conditional, then it turns out we have defined two different overloads. Then when to_long is called, we will get a "call is ambiguous" error.  ( See https://godbolt.org/z/tEH-pq )
</p>

<p>
Despite this theoretical impact, the actual impact is quite low; thankfully programmers almost never declare a function in terms of one templated type, and then define it with another.
</p>

<h2>Impact on the Standard</h2>
<p>
This proposal suggests that the while the type produced by the templated name_t variants of templated name_t classes must be identical, they are free to arrive at that type in any way the library author chooses.
</p>

<h2>Proposed Wording</h2>

<p>Note: All changes are relative to the 2023-01-04 working draft of C++23.</p>

<p>Modify 22.4.2 [tuple.syn] :</p>

<blockquote>
<pre>
  template&lt;size_t I, class T>
    using tuple_element_t = <ins>/* produces same type as </ins>typename tuple_element&lt;I, T>::type <ins>*/</ins>;
</pre>
</blockquote>

<p>Modify 22.6.2 [variant.syn] :</p>

<blockquote>
<pre>
  template&lt;size_t I, class T>
    using variant_alternative_t = <ins>/* produces same type as </ins>typename variant_alternative&lt;I, T>::type <ins>*/</ins>;
</pre>
</blockquote>

<p>Modify 22.10.2 [functional.syn] :</p>

<blockquote>
<pre>
  template&lt;class T> using unwrap_ref_decay_t = <ins>/* produces same type as </ins>typename unwrap_ref_decay&lt;T>::type <ins>*/</ins>;
</pre>
</blockquote>

<p>Modify 21.3.3 [meta.type.synop] :</p>

<blockquote>
<pre>
  template&lt;class T>
    using remove_const_t    = <ins>/* produces same type as </ins>typename remove_const&lt;T>::type <ins>*/</ins>;
  template&lt;class T>
    using remove_volatile_t = <ins>/* produces same type as </ins>typename remove_volatile&lt;T>::type <ins>*/</ins>;
  template&lt;class T>
    using remove_cv_t       = <ins>/* produces same type as </ins>typename remove_cv&lt;T>::type <ins>*/</ins>;

  template&lt;class T>
    using remove_reference_t     = <ins>/* produces same type as </ins>typename remove_reference&lt;T>::type <ins>*/</ins>;

  template&lt;class T>
    using make_signed_t   = <ins>/* produces same type as </ins>typename make_signed&lt;T>::type <ins>*/</ins>;
  template&lt;class T>
    using make_unsigned_t = <ins>/* produces same type as </ins>typename make_unsigned&lt;T>::type <ins>*/</ins>;

  template&lt;class T>
    using remove_extent_t      = <ins>/* produces same type as </ins>typename remove_extent&lt;T>::type <ins>*/</ins>;
  template&lt;class T>
    using remove_all_extents_t = <ins>/* produces same type as </ins>typename remove_all_extents&lt;T>::type <ins>*/</ins>;

  template&lt;class T>
    using remove_pointer_t = <ins>/* produces same type as </ins>typename remove_pointer&lt;T>::type <ins>*/</ins>;

  template&lt;size_t Len, size_t Align = default-alignment> // see [meta.trans.other]
    using aligned_storage_t  = <ins>/* produces same type as </ins>typename aligned_storage&lt;Len, Align>::type <ins>*/</ins>;
  template&lt;size_t Len, class... Types>
    using aligned_union_t    = <ins>/* produces same type as </ins>typename aligned_union&lt;Len, Types...>::type <ins>*/</ins>;
  template&lt;class T>
    using remove_cvref_t     = <ins>/* produces same type as </ins>typename remove_cvref&lt;T>::type <ins>*/</ins>;
  template&lt;class T>
    using decay_t            = <ins>/* produces same type as </ins>typename decay&lt;T>::type <ins>*/</ins>;
  template&lt;bool b, class T = void>
    using enable_if_t        = <ins>/* produces same type as </ins>typename enable_if&lt;b, T>::type <ins>*/</ins>;
  template&lt;bool b, class T, class F>
    using conditional_t      = <ins>/* produces same type as </ins>typename conditional&lt;b, T, F>::type <ins>*/</ins>;
  template&lt;class... T>
    using common_type_t      = <ins>/* produces same type as </ins>typename common_type&lt;T...>::type <ins>*/</ins>;
  template&lt;class... T>
    using common_reference_t = <ins>/* produces same type as </ins>typename common_reference&lt;T...>::type <ins>*/</ins>;
  template&lt;class T>
    using underlying_type_t  = <ins>/* produces same type as </ins>typename underlying_type&lt;T>::type <ins>*/</ins>;
  template&lt;class Fn, class... ArgTypes>
    using invoke_result_t    = <ins>/* produces same type as </ins>typename invoke_result&lt;Fn, ArgTypes...>::type <ins>*/</ins>;
</pre>
</blockquote>

<h2>Last words</h2>

<p>
The author is unaware of observable impact of changing the "_v" constexpr definitions, such as tuple_size_v, to be computed in some other way, so no proposal is made to change them.
</p>

</body>
</html>
