书中似乎有矛盾"C++ Templates - The Complete Guide"

There seems to be a contradiction in the book "C++ Templates - The Complete Guide"

本文关键字:Templates The Complete Guide C++ 矛盾      更新时间:2023-10-16

在《c++模板-完整指南》一书的2.4 Overloading Function Templates节中,您将找到以下示例:

// maximum of two int values
inline int const& max (int const& a, int const& b)
{
    return a < b ? b : a;
}
// maximum of two values of any type
template <typename T>
inline T const& max (T const& a, T const& b)
{
    return a < b ? b : a;
}
// maximum of three values of any type
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
    return max (max(a,b), c);
}
int main()
{
    ::max(7, 42); // calls the nontemplate for two ints (1)
}

然而,在B.2附录B的简化重载解析中,作者声明:

注意重载解析发生在模板实参演绎之后,…(2)

根据(2), ::max(7,42)应通过论证演绎调用max<int>

如果两个函数都是精确匹配的,包括cv限定符,编译器将在重载解析中选择非模板函数。

因此这里将选择非模板,即使模板化的函数本质上是相同的。

编辑:

在标准草案N3485中,我发现了这个:

13.3.3最佳可行函数[over.match.best]

给定这些定义,如果对于所有参数i, ICSi(F1)不是比ICSi(F2)更差的转换序列,则可行函数F1被定义为比另一个可行函数F2更好的函数,然后

- F1是非模板函数, F2是函数模板专门化,如果不是,则

- F1和F2是函数模板专门化,根据14.5.6.2中描述的偏序规则,F1的函数模板比F2的函数模板更专门化。

根据(2), ::max(7,42)应该通过参数推导调用max<int>

。想想看,要进行重载解析(也就是选择最佳匹配),编译器首先需要知道所有可用的重载,并能够对它们进行比较。注意,函数模板本身并不是一个有效的函数,它首先需要被实例化来创建一个真正的函数。

这意味着编译器首先检查所有名为max的函数模板,对它们中的每一个都尝试模板实参推导,然后对实例化的函数和非模板函数一起进行重载解析。

下面是函数的可视化(为了简洁,省略cv-限定符):

                    int max(int, int);
template<class Arg> Arg max(Arg, Arg);
template<class Arg> Arg max(Arg, Arg, Arg);

让我们来看看::max(7, 42)的调用。

首先,编译器看到有三个候选函数。然而,它不能只比较max的第一个过载和其他两个过载——这就像比较苹果和橘子一样。相反,它首先需要"戳穿"。一个真实的函数来自函数模板的蓝图。在本例中,这是通过模板实参演绎实现的:

int max(int, int); // originally non-template
int max(int, int); // originally template
int max(int, int, int); // not enough arguments, invalid

由于实参/形参计数不匹配而抛出第三个重载,我们只剩下两个。从重载解析的角度来看,两者是相等的——但是等等!现在出现了一个规则,它说:

§13.3.3 [over.match.best] p1

[…基于这些定义,如果[…]:

,则可行函数F1被定义为比另一个可行函数F2更好的函数。
  • F1是非模板函数,F2是函数模板专门化,

模板参数演绎后,

1) inline int const& max (int const& a, int const& b);

2) template <>
   inline int const& max (int const& a, int const& b)

在这种情况下,1)按照c++标准13.3.3 par. 1 (Draft n3092)的规定调用。

Msdn也明确指出:

如果非模板函数与模板函数同样匹配,则选择非模板函数

http://msdn.microsoft.com/en-us/library/s016dfe8%28v=vs.80%29.aspx

c++ 11标准第13.3.3/1段(重载解析上下文中的"最佳可行函数"):

定义ICSi(F)如下:

—如果F是静态成员函数,则定义ICS1 (F)使ICS1 (F)不优于也不劣于ICS1 (G)对于任何函数G,并且,对称地,ICS1 (G)既不优于也不劣于ICS1 (F)132;否则,

-设ICSi(F)表示将列表中的第i个参数转换为对象的隐式转换序列可行函数f的第i个参数的类型。13.3.3.1定义了隐式转换序列和13.3.3.2定义了一个隐式转换序列是一个更好的转换序列意味着什么

给定这些定义,一个可行函数F1被定义为比另一个可行函数更好的函数F2如果对于所有参数i, ICSi(F1)不是比ICSi(F2)更差的转换序列,然后

-对于某些参数j, ICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果不是,

——上下文是用户自定义转换的初始化(参见8.5、13.3.1.5和13.3.1.6)从返回类型F1到目的类型(即,类型)的标准转换序列实体被初始化)是一个比标准转换序列更好的转换序列将F2的返回类型转换为目标类型。[…]

- F1是非模板函数,F2是函数模板专门化,如果不是,则

- F1和F2是函数模板专门化,F1的函数模板更专门化

根据14.5.6.2中描述的部分排序规则,将F2的模板替换。

这意味着在重载解析上下文中,当通过实例化函数模板生成的函数(因此,在类型推导之后)与非模板函数同样匹配时,首选非模板函数。

相关文章: