【问题标题】:There seems to be a contradiction in the book "C++ Templates - The Complete Guide"《C++ Templates - The Complete Guide》这本书似乎有矛盾之处
【发布时间】:2013-03-04 18:14:43
【问题描述】:

在“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 的 B.2 简化重载解决方案中,作者指出:

注意重载解析发生在模板参数推导之后,...(2)

根据(2)::max(7,42)应该通过参数推导调用max&lt;int&gt;

【问题讨论】:

  • 当在模板参数推导后完成重载决议时,为什么它应该更喜欢另一个呢?在这一点上它们本质上是相同的,但是我认为编译器会选择最适合的那个,并且 AFAIK 更喜欢非模板而不是模板。
  • 为什么(2)暗示max&lt;int&gt;?您已经推导出了模板参数,但您仍然需要解决重载问题,包括非模板函数。

标签: c++ templates template-argument-deduction


【解决方案1】:

如果函数都完全匹配,编译器将在重载解析中优先选择非模板函数,包括cv 限定符。

因此这里会选择非模板,即使模板化的函数本质上是一样的。

编辑:

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

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

鉴于这些定义,如果对于所有参数 i,ICSi(F1) 不是比 ICSi(F2 ),然后

...

——F1 是一个非模板函数,F2 是一个函数模板特化,或者,如果不是这样,

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

...

【讨论】:

  • 13.3.3 [over/match.best] 第 1 段说非模板是更好的匹配
【解决方案2】:

根据(2)::max(7,42)应该通过参数推导调用max&lt;int&gt;

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

这意味着,首先,编译器会查看所有名为 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 更好的函数 if [...]:

  • F1 是非模板函数,F2 是函数模板特化,

【讨论】:

    【解决方案3】:

    模板参数推导后发生重载解析

    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 中的规定调用。 1 (Draft n3092)。

    Msdn 也明确说明:

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

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

    【讨论】:

    • 您的答案对于 MS 编译器是正确的。其他人呢?
    • 您应该检查标准的内容,而不是尝试为每个编译器查找参考。 MSVC 遵循标准,如果它们在其他方面同样好匹配,则首选非模板而不是模板。见 13.3.3 [over.match.best] 第 1 段
    • @JonathanWakely 当然。但实际上我已经添加了对标准的引用。
    • 我接受你的回答,主要是因为这是第一个让我了解这里发生了什么的人。
    • @log0,嗯,事实上我的评论在你添加参考之前 12 分钟就已经存在了 :)
    【解决方案4】:

    根据 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 的模板。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-13
      • 2015-08-25
      • 2017-11-06
      • 2019-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多