title: Types of elements of a tuple view
body: <p>I'm designing a utility to create a <em>view</em> into a tuple-like object. The goal is to enable writing code like this:</p>
  
  <pre><code>// make_tuple_view&lt;Is...&gt;(t): view of t's elements at positions Is...
  
  auto t = std::make_tuple(42, 'c', 3.14);
  auto tv = make_tuple_view&lt;2, 1, 0, 1&gt;(t);  // tv == (3.14, 'c', 42, 3.14)
  ++std::get&lt;0&gt;(tv);
  // tv == (4.14, 'c', 42, 4.14)
  // t  == (42, 'c', 4.14)
  </code></pre>
  
  <p>So the element types of a tuple view are appropriate types of references to the corresponding elements in the original tuple. Currently this is how I deduce the reference types:</p>
  
  <pre><code>#include &lt;tuple&gt;
  #include &lt;utility&gt;
  
  namespace detail {
  
  template&lt;std::size_t FromIndex, std::size_t... Is, typename Tuple&gt;
  constexpr auto make_tuple_range_impl(std::index_sequence&lt;Is...&gt;,
                                       Tuple&amp;&amp; t) noexcept
  {
      return std::forward_as_tuple(
              std::get&lt;FromIndex + Is&gt;(std::forward&lt;Tuple&gt;(t))...);
  }
  
  }  // namespace detail
  
  // make_tuple_range
  template&lt;std::size_t FromIndex, std::size_t ToIndex, typename Tuple&gt;
  constexpr auto make_tuple_range(Tuple&amp;&amp; t) noexcept
  {
      static_assert(FromIndex &lt;= ToIndex,
                    "FromIndex must be less than or equal to ToIndex");
  
      return detail::make_tuple_range_impl&lt;FromIndex&gt;(
              std::make_index_sequence&lt;ToIndex - FromIndex&gt;(),
              std::forward&lt;Tuple&gt;(t));
  }
  
  // make_tuple_view
  template&lt;std::size_t... Is, typename Tuple&gt;
  constexpr auto make_tuple_view(Tuple&amp;&amp; t) noexcept
  {
      return std::forward_as_tuple(std::get&lt;Is&gt;(std::forward&lt;Tuple&gt;(t))...);
  }
  </code></pre>
  
  <p><a href="https://godbolt.org/g/hE6Vha" rel="nofollow">Tests</a>:</p>
  
  <pre><code>double pi = 3.14;
  std::tuple&lt;int, double&amp;, const char, float&gt; t(42, pi, 'c', 0);
  
  // non-const lvalue
  static_assert(std::is_same&lt;
                    decltype(make_tuple_range&lt;0, 3&gt;(t)),
                    std::tuple&lt;int&amp;, double&amp;, const char&amp;&gt;
                &gt;::value, "");
  
  // const lvalue
  const auto&amp; ct = t;
  static_assert(std::is_same&lt;
                    decltype(make_tuple_view&lt;3, 0, 2, 1&gt;(ct)),
                    std::tuple&lt;const float&amp;, const int&amp;, const char&amp;, double&amp;&gt;
                &gt;::value, "");
  
  // non-const rvalue
  static_assert(std::is_same&lt;
                    decltype(make_tuple_range&lt;1, 4&gt;(std::move(t))),
                    std::tuple&lt;double&amp;, const char&amp;&amp;, float&amp;&amp;&gt;
                &gt;::value, "");
  
  // const rvalue
  const auto&amp;&amp; crt = std::move(t);
  static_assert(std::is_same&lt;
                    decltype(make_tuple_range&lt;1, 4&gt;(std::move(crt))),
                    std::tuple&lt;double&amp;, const char&amp;, const float&amp;&gt;
                &gt;::value, "");
  </code></pre>
  
  <p>However, I'm not entirely confident in my implementation. Is there any edge case where incorrect reference types can be deduced?</p>
  
  <p><strong>UPDATE</strong>: Thanks for all the awesome answers, TIL for sure! However, I'm still wondering if my definition of <code>make_tuple_view/range</code> can deduce incorrect return types. The purpose of this question is not so much to look for the optimal implementation as to learn about the details of this part of the type system. I'd really appreciate it if more focus can be put on the deduced return type for my <code>make_tuple_view/range</code> functions.</p>