多个部分专用化与模板参数列表匹配

more than one partial specialization matches the template argument list

本文关键字:参数 列表 个部 专用      更新时间:2024-09-28

当我运行下面的程序时,我收到一个错误,说有多个专业化与类X<int,int*,10>。编译器显然不能决定是否使用";专业化1";或";专业化2";。我不明白为什么会出现这种情况,因为我认为编译器根据模板参数的数量来确定专业化级别。因此,由于";专业化1";具有2个模板参数(template<class T, int I>(;专业化2";只有1个模板参数(template<class T>(;专业化2";比";专业化1";。显然,我的推理是不正确的,那么有人能解释推断哪个模板更专业背后的逻辑吗?


#include <iostream>
using namespace std;

template<class T, class U, int I> struct X {
void f() { cout << "General Template" << endl; }
};

template<class T, int I> struct X<T, T*, I> {
void f() { cout << "Specialization 1" << endl; }
};

template<class T> struct X<int, T*, 10> {
void f() { cout << "Specialization 2" << endl; }
};

int main() {
X<int, int*, 10> f;
f.f();
return 0;
}

不幸的是,这个主题非常复杂!我是基于这个答案和cppreference,同时引用该标准的当前工作草案。

首先,根据";部分专业化的匹配":

如果部分专门化的模板参数可以从实际模板参数列表推导出来,并且推导出的模板参数满足部分专门化(如果有的话(的相关约束,则部分专门化与给定的实际模板参数表匹配。

在您的示例中,上下文是语句X<int, int*, 10> f;,它匹配两个部分专用化:

template<class T, int I> struct X<T, T*, I> {...}    // -> T = int, I = 10
template<class T>        struct X<int, T*, 10> {...} // -> T = int

既然是这样,就需要进一步检查,以确定这些候选人中的一个是否比另一个更专业。这在";部分专业化的部分排序":

对于两个部分专业化,第一个比第二个更专业化如果对两个函数模板进行以下重写,根据函数模板的排序规则,第一个函数模板比第二个子模板更专业化:

  • 这两个函数模板中的每一个都具有与相应的部分专门化相同的模板参数和相关约束
  • 每个函数模板都有一个函数参数,其类型为类模板专用化,其中模板参数是部分专用化的简单模板idtemplate argument list中每个模板参数的函数模板中的相应模板参数

这很冗长,但正如示例所示,它只是将部分专业化写为函数参数:

template<class T, int I> void f(X<T, T*, I>);    // #1
template<class T>        void f(X<int, T*, 10>); // #2

从那里我们必须看到关于";函数模板的部分排序";。引用相关段落:

偏序通过依次转换每个模板(见下一段(并使用函数类型执行模板参数推导来选择两个函数模板中哪一个比另一个更专业。[…]

为了生成转换后的模板,对于每个类型、非类型或模板模板参数(包括其模板参数包(,分别合成一个唯一的类型、值或类模板,并将其替换为该参数在模板的函数类型中的每次出现。

使用转换后的函数模板的函数类型,对另一个模板执行类型推导,如[temp.dexecute.partial]中所述。

换句话说,将#1和#2(再次(变换为"0";实例化";每个都有唯一的自变量,也就是说,不同于任何其他存在的自变量:

void f(X<Ut1, Ut1*, Uv1>); // #A
void f(X<int, Ut2*, 10>);  // #B

然后使用他们的";"函数类型";,作为返回类型和参数类型,根据另一个部分执行推导,";在部分排序期间推导模板参数":

推导过程使用转换后的类型作为参数模板,另一个模板的原始类型作为参数模版。对于偏序比较中涉及的每个类型,此过程要执行两次:一次使用转换后的模板-1作为参数模板,模板-2作为参数模板;另一次使用转化后的模板-2作为自变量模板,模板-1作为参数模板。

好的,通过cppreference上的示例:

// #1 from #B: 
// void(X<T, T*, I>) from void(X<int, Ut2*, 10>): deduction fails
// #2 from #A:
// void(X<int, T*, 10>) from void(X<Ut1, Ut1*, Uv1>): deduction fails

至少,我认为是这样的,因此整个磨难的结果是,两个模板都比另一个模板更专业。