编译时间哈希 - 模板扣除顺序的混淆

时间:2016-11-09 23:16:10

标签: c++11 templates template-meta-programming

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

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>始终会调用自身,是什么导致hash_calc<N, N>I到达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);
}

1 个答案:

答案 0 :(得分:0)

  

我完全不知道为什么它不会成为无限递归调用?

让我们一步一步地看一下。

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==1I==N,直到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;
  };
};

被调用。递归就结束了。

  

为什么?!

<N,I>

是更一般的情况,它处理任何一对template<size_t N> struct hash_calc<N, N> { static constexpr size_t apply(const char(&s)[N]) { return 2166136261u; }; }; ,而

<N,N>

是它的专业化,它只能处理this的情况。 编译器首先需要一般情况,然后是特殊情况。