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

时间:2012-06-21 06:19:39

标签: c++ templates compiler-errors template-specialization

比较班级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.\r\n", qq.spec);
}

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

  

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

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

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

3 个答案:

答案 0 :(得分:5)

该标准具体说明只有在您尝试实例化模板时才会发生这种情况(§14.5.4.1/ 1):

  

当在需要实例化类的上下文中使用类模板时,有必要确定是使用主模板还是部分特化之一生成实例化。 [强调补充]

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

  

这是通过将类模板特化的模板参数与部分特化的模板参数列表进行匹配来完成的。

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

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

现在,这些专业化都不会被使用,这是肯定的,因为总是存在歧义 - 如果要么匹配,另一个显然匹配得很好。但是,编译器根本不需要检测或诊断它。在这个确切的情况下(基本上相同的专业化)可能很容易,但几乎可以肯定其他情况会更加困难,所以(显然)委员会认为编译器甚至不必尝试。

答案 1 :(得分:1)

啊,但他们相同,因为他们不使用相同的参数。使用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.\r\n", 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 *>'

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

答案 2 :(得分:0)

  

对我而言,为尝试发出错误更合乎逻辑   声明相同的模板特化。

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

以下是我的猜测:
编译器可能不会对template主体执行额外的完整性/常识性检查。
实例化template后,编译器会查找匹配类型并选择最佳匹配类型。如果它与仅一个不匹配,则会为 no-match 多个匹配提供编译器错误。

例如:

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

int main ()
{
}

编译正常,即使我们知道在整个C ++程序中没有单个函数名x()。只有在您尝试实例化foo<T>时,编译器才会出错。

此外,如果您通过将main()放在两个专精之间来扭曲您的示例,它将compile perfectly fine