为什么 template<classTp> bool is_array<Tp[]> 是 template<class T> bool is_array<Tp> 的偏特化?

Why is template<classTp> bool is_array<Tp[]> a partial specialization for template<class T> bool is_array<Tp>?

如何理解 template<typename Tp> bool is_array<Tp[]> = truetemplate<typename T> bool is_array<Tp> = true 的偏特化?

这是相关的code snippet:

#include<iostream>

template<typename T>
bool is_array = false;

template<typename Tp>
bool is_array<Tp[]> = true;

int main()
{
    std::cout << is_array<int> << std::endl;
    std::cout << is_array<int[]> << std::endl;
}

我还注意到,一般来说,部分模板特化中的模板参数数量少于主模板中的模板参数数量。

偏特化通常看起来像 this:

#include<iostream>

template<typename T, typename U>
class add
{
public:
    add(T x, U y)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

template<typename U>
class add<int, U>
{
    public:
    add(int x, U y)
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;       
    }
};

int main()
{
    add<int, double>(1, 5.0);
    add<char, int>('a', 9);
}

这是编译器为您的代码生成的中间输出(参考 cppinsights

编译器正在使用数组的专用模板。

template<typename T>
bool is_array = false;

template<>
bool is_array<int> = false;
template<>
bool is_array<int []> = true;

template<typename Tp>
bool is_array<Tp[]> = true;

int main()
{
  std::cout.operator<<(is_array<int>).operator<<(std::endl);
  std::cout.operator<<(is_array<int []>).operator<<(std::endl);
}

现在如果删除下面的代码

template<typename Tp>
bool is_array<Tp[]> = true;

编译器会生成

template<typename T>
bool is_array = false;

template<>
bool is_array<int> = false;
template<>
bool is_array<int []> = false;

int main()
{
  std::cout.operator<<(is_array<int>).operator<<(std::endl);
  std::cout.operator<<(is_array<int []>).operator<<(std::endl);
}

你可以看到编译器只是简单地将T替换为int[],这只不过是你提供的第二个模板,一个专门的模板。

要使模板成为 主要专业化,它需要比主要的更专业。这基本上意味着专业化必须匹配主要可以匹配的类型的严格子集

在您的情况下,特化会匹配 int[]char[] 等。主要也会匹配这些类型,但主要会另外匹配 intcharstd::vector<std::string>

当像这样 is_array<int[]> 指定模板参数时,规则规定首先尝试专业化。在这种情况下,Tp[] 可以匹配到 int[](基本上是用 int 代替 Tp),因此选择了专业化。

当指定这样的模板参数时 is_array<int>,首先尝试特化,但匹配失败。然后尝试了主要模板,并且匹配,因此选择了主要模板。

正如您所提到的,通常情况下专业化具有较少的模板参数。这不是必需的,只要我上面提到的规则适用,即特化必须匹配主要可以匹配的类型的严格子集。

事实上,特化甚至可以比主有更多的模板参数。同样,要求只是专业化匹配主要匹配的类型的子集。

专业化甚至可以比主要有更多的模板参数。这是一个 example:

#include<iostream>

template<typename T>
bool is_array = false;
    
template <typename Tp, int N>
bool is_array<Tp[N]> = true;

int main()
{
    std::cout << is_array<int> << std::endl;
    std::cout << is_array<int[5]> << std::endl;
}