Yet another hash tuple in C++17 Is there something terribly wrong with this implementation?

template &lt;class T&gt;
constexpr inline std::size_t hash_combine(T const&amp; v,
  std::size_t const seed = {}) noexcept
{
  return seed ^ (std::hash&lt;T&gt;()(v) + 0x9e3779b9 + (seed &lt;&lt; 6) + (seed &gt;&gt; 2));
}

template &lt;typename T&gt; struct hash;

template &lt;typename ...T&gt;
struct hash&lt;std::tuple&lt;T...&gt;&gt;
{
  template &lt;typename A1, typename ...A, std::size_t ...I&gt;
  static auto apply_tuple(std::tuple&lt;A1, A...&gt; const&amp; t,
    std::index_sequence&lt;I...&gt;) noexcept
  {
    if constexpr(sizeof...(A))
    {
      return hash_combine(std::get&lt;0&gt;(t),
        hash&lt;std::tuple&lt;A const&amp;...&gt;&gt;()({std::get&lt;I + 1&gt;(t)...})
      );
    }
    else
    {
      return std::hash&lt;std::remove_cv_t&lt;std::remove_reference_t&lt;A1&gt;&gt;&gt;()(
        std::get&lt;0&gt;(t));
    }
  }

  auto operator()(std::tuple&lt;T...&gt; const&amp; t) const noexcept
  {
    return apply_tuple(t,
      std::make_index_sequence&lt;sizeof...(T) - 1&gt;()
    );
  }
};


EDIT: This is better, I think:

template &lt;typename ...T&gt;
struct hash&lt;std::tuple&lt;T...&gt;&gt;
{
  template &lt;std::size_t ...I&gt;
  static auto apply_tuple(std::tuple&lt;T...&gt; const&amp; t,
    std::index_sequence&lt;I...&gt;)
  {
    std::size_t seed{};

    return ((seed = hash_combine(std::get&lt;I&gt;(t), seed)), ...);
  }

  auto operator()(std::tuple&lt;T...&gt; const&amp; t) const
  {
    return apply_tuple(t, std::make_index_sequence&lt;sizeof...(T)&gt;());
  }
};
Your code doesn't handle empty tuples gracefully.  The first version generates a compilation error, and the second version returns void.  For the first version, you can add an apply_tuple overload for empty tuples.  For the second version, you can change

return ((seed = hash_combine(std::get&lt;I&gt;(t), seed)), ...);


to

((seed = hash_combine(std::get&lt;I&gt;(t), seed)), ...);
return seed;




Is 0 really a good initial seed?  Some prime number, I guess, might be better (e.g., 672807365, which is what MSVC returns for std::hash&lt;std::nullptr_t&gt;{}(nullptr)).



The tuple unpacking can be simplified with std::apply, avoiding index_sequences:

template &lt;typename... Args&gt;
struct hash&lt;std::tuple&lt;Args...&gt;&gt; {
    std::size_t operator()(const std::tuple&lt;Args...&gt;&amp; tuple) const noexcept
    {
        return std::apply([](const auto&amp;... args) {
            auto seed = static_cast&lt;std::size_t&gt;(672807365);
            ((seed = hash_combine(args, seed)), ...);
            return seed;
        }, tuple);
    }
};


(live demo)