编译时哈希 - 模板推导顺序混乱

Compile time hash - confusion on template deduction order

我在其他主题中找到了这段代码

template<size_t N, size_t I = 0>
struct hash_calc
{
  static constexpr size_t apply(const char(&s)[N])
  {
    return  (hash_calc < N, I + 1 >::apply(s) ^ s[I]) * 16777619u;
  };
};

template<size_t N>
struct hash_calc<N, N>
{
  static constexpr size_t apply(const char(&s)[N])
  {
    return 2166136261u;
  };
};

template<size_t N>
constexpr size_t hash(const char(&s)[N])
{
  return hash_calc<N>::apply(s);
}

首先,我完全不明白为什么它不会以无限递归调用结束?据我了解,首先 hash_calc<N, I> 总是会调用自己,是什么导致它在 I 到达 N 时在 hash_calc<N, N> 中断? hash_calc<N, I> 的模板实例化在 N == I 时不会失败。 其次 - 我试图将 hash_calc<N, N> 结构复制粘贴到 hash_calc<N, I> 上,这会导致编译错误 error: 'hash_calc' is not a class template struct hash_calc<N, N>。这是为什么?!

编辑: 下面修改后的代码不会在 msvc 或 gcc 下编译。我刚刚将一个模板放在另一个模板之上。发生什么事了?

template<size_t N>
struct hash_calc<N, N>
{
    static constexpr size_t apply(const char(&s)[N])
    {
        return 2166136261u;
    };
};

template<size_t N, size_t I = 0>
struct hash_calc
{
  static constexpr size_t apply(const char(&s)[N])
  {
    return  (hash_calc < N, I + 1 >::apply(s) ^ s[I]) * 16777619u;
  };
};

template<size_t N>
constexpr size_t hashq(const char(&s)[N])
{
  return hash_calc<N>::apply(s);
}

I am totally confused why it does not end being an infinite recursive call?

那么让我们一步步来看吧。

hashq<N>(const char(&s)[N]) 呼叫 hash_calc<N>::apply(s);.

那是:

template<size_t N, size_t I = 0>
struct hash_calc
{
  static constexpr size_t apply(const char(&s)[N])
  {
    return  (hash_calc < N, I + 1 >::apply(s) ^ s[I]) * 16777619u;
  };
};

I==0。 现在 hash_calc < N, I + 1 >::apply(s) 被调用,I==1,这一直持续到 I==N。那时

template<size_t N>
struct hash_calc<N, N>
{
    static constexpr size_t apply(const char(&s)[N])
    {
        return 2166136261u;
    };
};

被调用。并且递归结束。

Why is that?!

template<size_t N, size_t I = 0>
struct hash_calc
{
  static constexpr size_t apply(const char(&s)[N])
  {
    return  (hash_calc < N, I + 1 >::apply(s) ^ s[I]) * 16777619u;
  };
};

是更一般的情况,它处理任何一对<N,I>,而

template<size_t N>
struct hash_calc<N, N>
{
    static constexpr size_t apply(const char(&s)[N])
    {
        return 2166136261u;
    };
};

是它的特长,它只能处理<N,N>的情况。 编译器首先需要一般情况,然后是特殊情况。