考虑这个例子
struct A { };
template<class T> struct B {
template<class R> int operator*(R&); // #1
};
template<class T, class R> int operator*(T&, R&); // #2
部分排序将应用于#1
和#2
以选择最佳可行的函数模板。
使用两组类型来确定部分排序。对于所涉及的每个模板,都有原始函数类型和转换后的函数类型。演绎过程使用转换后的类型作为参数模板,另一个模板的原始类型作为参数模板。这个过程对部分排序比较中涉及的每种类型进行两次:一次使用转换后的模板-1作为参数模板,模板-2作为参数模板,再次使用转换后的模板-2作为参数模板,模板-1作为参数模板。
部分排序通过依次转换每个模板来选择两个函数模板中的哪一个比另一个更专业(见下一段)
为了生成转换后的模板,对于每个类型、非类型或模板模板参数(包括其模板参数包),分别合成一个唯一的类型、值或类模板,并将其替换为模板的函数类型中该参数的每次出现。【注:为非类型模板参数合成的值的类型中替换占位符的类型也是唯一的合成类型。——尾注】如果函数模板M中只有一个是某个类A的非静态成员,则认为M在其函数参数列表中插入了新的第一个参数。给定cv作为M的cv限定符(如果有),如果M的可选ref限定符是,则新参数的类型为“对cv A的右值引用”
因此,#2
的原始类型是int运算符*(T
该规则的结构似乎应该将上述规则中强调的部分视为生产转换模板的步骤。
因此,#1
的原始类型是否为int运算符*(B
如何正确阅读生成转换模板的规则?如果强调部分不被视为转换的一个步骤,而是部分排序过程中成员函数的通用规则,该规则是否会导致将此类规则放置在转换过程之后具有误导性?
是的,有点乱。正如您所观察到的,类非静态成员函数的“原始类型”缺少插入的this
参数是没有意义的。
使其发挥作用的唯一方法是让第3小节中的后续段落适用于[temt. denet.部分]另一边的“原始”函数类型以及“转换”函数类型。你可以这样理解第二段目前的第一句,将“每个”理解为同时适用于转换后的函数类型和原始函数类型:
作为成员函数的每个函数模板M都被认为具有X(M)[…]类型的新第一个参数
然而,自P2108中对CWG 2445的决议以来,我们还有另一句话:
如果恰好其中一个函数模板被认为是[…]通过参数顺序相反的重写候选,则其转换模板中函数参数的顺序是相反的。
所以我们很清楚这种反转是不对称应用的,给出了一个荒谬的结果。从好的方面来说,它应该如何阅读是相当清楚的;对函数类型的调整(this
插入和参数反转)应该在唯一类型合成/替换之前应用,并且同样应用于“原始”和转换后的函数类型。
据我所知,这个缺陷似乎没有报告给核心语言工作组;它没有出现在C标准核心语言活动问题列表中。