为什么编译器在定义类似的模板专用化时不会出错?

Why compiler doesn't give error while defining similar template specializations?

本文关键字:专用 出错 编译器 定义 为什么      更新时间:2023-10-16

比较班级template专业的程序是什么?标准在这一点上没有详细说明(或者我错过了正确的地方)。
我的问题与决定在实例化期间使用哪种专业化无关。请不要对此发表评论。问题是关于相互比较专业化,以确定特定专业化是否已经定义。

请考虑以下示例代码:

template <class x1, class x2>
struct CoreTemplate { };
template <class x1, class x2>
struct CoreTemplate<x1*, x2*> { int spec; CoreTemplate() { spec = 1; } };
template <class x1, class x2>
struct CoreTemplate<x2*, x1*> { int spec; CoreTemplate() { spec = 2; } };
int main(int argc, char* argv[])
{
    CoreTemplate<int*, int*> qq;
    printf("var=%d.rn", qq.spec);
}

当我尝试使用 MSVC 编译此代码时,我在 main 函数内出现实例化尝试的错误:

cpptest1.cxx(15):错误 C2752: ' CoreTemplate<x1,x2> ' : 多个部分专用化与模板参数列表匹配

对我来说,为尝试声明相同的模板专业化而发出错误会更合乎逻辑。我认为上述专业之间没有任何区别。

那么,有人知道比较模板专业化的规则吗?文章、链接、书籍等也会有所帮助。

该标准明确指出,这仅在您尝试实例化模板时发生 (§14.5.4.1/1):

在需要类实例化的上下文中使用类模板时,有必要确定是使用主模板还是使用部分专用化之一生成实例化。[着重号后加]

不幸的是,如果不讨论如何决定在实例化期间使用哪种专用化,就无法回答其余的问题。以下是标准中的文本(继续上面的摘录):

这是通过将类模板专用化的模板参数

与部分专用化的模板参数列表进行匹配来完成的。

  • 如果只找到一个匹配的专用化,则会从该专用化生成实例化。
  • 如果找到多个匹配的专用化,则使用偏序规则 (14.5.4.2) 来确定其中一个专用化是否比其他专用化更专业。如果没有一个专用化比所有其他匹配的专用化更专业,则类模板的使用是不明确的,并且程序格式不正确。

因此,它甚至从未尝试将模板直接相互比较。相反,它试图找到一种与给定参数相匹配的专业化。如果有多个匹配,它将尝试根据部分排序规则选择最专业的一个。如果两者都不比另一个更专业,则实例化不明确,编译失败。

现在,这些专业都不能使用,因为总是会有歧义 - 如果任何一个匹配,另一个显然同样匹配。不过,编译器根本不需要检测或诊断这一点。在这种确切的情况下(本质上相同的专业),这可能很容易,但几乎可以肯定的是,在其他情况下会困难得多,所以(显然)委员会决定编译器甚至不必尝试。

啊,但它们不一样,因为它们不使用相同的参数。使用 clang 和您的原始示例:

#include <cstdio>
template <class x1, class x2>
struct CoreTemplate { };
template <class x1, class x2>
struct CoreTemplate<x1*, x2*> { int spec; CoreTemplate() { spec = 1; } };
// note: partial specialization matches [with x1 = int, x2 = int]
template <class x1, class x2>
struct CoreTemplate<x2*, x1*> { int spec; CoreTemplate() { spec = 2; } };
// note: partial specialization matches [with x1 = int, x2 = int]
int main()
{
    CoreTemplate<int*, int*> qq;
    // error: ambiguous partial specializations of 'CoreTemplate<int *, int *>'
    std::printf("var=%d.rn", qq.spec);
}

但是,如果我们调整部分专业化,使它们完全匹配:

template <class x1, class x2>
struct Core { };
template <class x1>
struct Core<x1*, x1*> { int spec; Core() { spec = 1; } };
// note: previous definition is here
template <class x1>
struct Core<x1*, x1*> { int spec; Core() { spec = 2; } };
// error: redefinition of 'Core<type-parameter-0-0 *, type-parameter-0-0 *>'

因此,这似乎只是一个实施质量问题。编译器可能会针对第一种情况发出警告,但在一般情况下可能会消耗资源,或者可能只是到目前为止没有人表示需要。

对我来说,为尝试发出错误会更合乎逻辑 声明相同的模板专用化。

这不会发生,因为CoreTemplate<int*, double*>CoreTemplate<double*, int*>将生成不同的类型。

以下是我的猜测:
编译器可能不会对template正文进行额外的理智/常识性检查。
一旦你实例化了一个template,编译器就会查找匹配的类型并选择最好的类型。如果它不只匹配一个,则它会为不匹配多重匹配提供编译器错误。

例如:

template<typename T>
void foo ()
{
  T::x();
}
int main ()
{
}

可以编译,即使我们知道在整个程序中C++没有一个函数名称x()。仅当您尝试实例化foo<T>时,编译器才会给出错误。

此外,如果您通过将main()放在两个专业之间来稍微扭曲您的示例,它将编译得很好。