C ++模板函数优先级

时间:2015-06-25 10:33:03

标签: c++ function templates overload-resolution

#include <iostream>

template <class U, class T>
void foo(U&, T&)
{
    std::cout << "first";
}

template <class T>
void foo(int&, const T&)
{
    std::cout << "second";
}

int main()
{
    int a;
    double g = 2.;
    foo(a, g); // prints "first"

    return 0;
}

要调用第二个foo重载,编译器只需要执行一次模板类型推导,但对于第一次重载,它需要执行两次。你能解释为什么要调用第一个重载吗?

3 个答案:

答案 0 :(得分:16)

过载分辨率分多步完成。

首先,通过名称查找,我们选择可行的候选人名单。在这种情况下,即:

template <class U, class T>
void foo(U&, T&);            // with U = int, T = double

template <class T>
void foo(int&, const T&)     // with T = double

接下来,我们确定每个可行候选者的每个参数所需的转换序列。这是[over.ics.rank]:

  

标准转换序列S1是比标准转换序列更好的转换序列   S2如果[...]

     
      
  • S1是S2的正确子序列(比较规范形式的转换序列   由13.3.3.1.1定义,不包括任何左值变换;身份转换序列是   被认为是任何非身份转换序列的子序列)或者,如果不是,
  •   
  • S1的等级优于S2的等级,或者S1和S2具有相同的等级并且是可区分的   根据以下段落中的规则,或者,如果不是,
  •   

对于第一次调用,转换序列为(Identity,Identity)。对于第二次调用,转换序列是(Identity,Identity)。所以我们在那里平等。这两个要点都没有区分这两个要求。所以我们继续前进。

  
      
  • S1和S2是引用绑定(8.5.3),并且都不引用a的隐式对象参数   声明的非静态成员函数没有ref-qualifier,S1绑定一个右值引用   右值和S2绑定左值参考值。
  •   

不相关。

  
      
  • S1和S2是引用绑定(8.5.3),S1将左值引用绑定到函数左值和   S2将右值引用绑定到函数左值。
  •   

不。

  
      
  • S1和S2的不同之处仅在于他们的资格转换,并且产生类似的类型T1和T2(4.4),   类型T1的cv资格签名是cv资格的适当子集   T2型签名。
  •   

资格转换是一个指针,nope。

  
      
  • S1和S2是引用绑定(8.5.3),引用引用的类型相同   除了顶级cv限定符之外的类型,以及由S2初始化的引用所引用的类型   比由S1初始化的引用引用的类型更符合cv。
  •   

在这种情况下,第一个重载将其第二个参数作为double&,而第二个重载采用const double&。前者比后者更少 cv - 因此我们在这里停止 - 更喜欢foo(U&,T&)

只有在确定哪个转换序列更好的步骤之后,我们才能进入更专业的模板首选的步骤。 [over.match.best]中的完整规则排序是:

  

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

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

这就是我们刚刚经历的事情。

  
      
  • 上下文是用户定义的转换初始化[...]
  •   
  • 上下文是通过转换函数初始化直接引用绑定[...]
  •   
  • F1不是功能模板专业化,F2是功能模板专业化,或者,如果不是,
  •   
  • F1和F2是功能模板专精,F1的功能模板更专业   根据14.5.6.2中描述的部分排序规则,比F2的模板。
  •   

这就是我们选择foo(U&, T&)的原因。但是,如果删除const,则所有步骤中的两个转换序列都是相同的 - 因此,此时,更专业的模板(foo(int&, T&))将获胜。

请注意,更专业的是确定最佳候选人的最后机制。这是最终的决胜局。

另请注意,模板扣除的编号无关紧要。 可能在选择作为模板的重载和不是模板的重载之间起作用 - 但是在具有 x 模板参数的重载之间进行选择并不重要。 y&gt;的重载x 模板参数。

答案 1 :(得分:7)

在第二个函数中声明第二个参数为const。下面的应用修正示例调用第二个示例:

#include <iostream>

template <class U, class T>
void foo(U&, T&)
{
    std::cout << "first";
}

template <class T>
void foo(int&, T&)
{
    std::cout << "second";
}

int main()
{
    int a;
    double g = 2.;
    foo(a, g);

    return 0;
}

另一方面,当您在const中明确声明第二个参数时,请main(),应用程序会按照预期在上面的示例中调用第二个函数:

#include <iostream>

template <class U, class T>
void foo(U&, T&)
{
    std::cout << "first";
}

template <class T>
void foo(int&, const T&)
{
    std::cout << "second";
}

int main()
{
    int a;
    const double g = 2.;
    foo(a, g);

    return 0;
}

答案 2 :(得分:3)

观察:

  • 第一个重载将非const值作为第二个参数
  • 第二个重载将const值作为第二个参数

因为您将g作为非常量左值传递,编译器会选择第一次重载。