C++ 模板函数优先级

C++ template functions priority

#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重载,编译器只需要执行一次模板类型推导,但对于第一个重载,它需要执行两次。你能解释一下为什么调用第一个重载吗?

观察:

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

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

您在第二个函数中声明第二个参数为 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;
}

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

#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;
}

过载解决是分多个步骤完成的。

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

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]:

Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...]

  • S1 is a proper subsequence of S2 (comparing the conversion sequences in the canonical form defined by 13.3.3.1.1, excluding any Lvalue Transformation; the identity conversion sequence is considered to be a subsequence of any non-identity conversion sequence) or, if not that,
  • the rank of S1 is better than the rank of S2, or S1 and S2 have the same rank and are distinguishable by the rules in the paragraph below, or, if not that,

第一次调用时,转换顺序为(Identity, Identity)。对于第二次调用,转换顺序为(Identity, Identity)。所以我们在那里是平等的。这些要点都不能区分这两个调用。所以我们继续前进。

  • S1 and S2 are reference bindings (8.5.3) and neither refers to an implicit object parameter of a non-static member function declared without a ref-qualifier, and S1 binds an rvalue reference to an rvalue and S2 binds an lvalue reference.

无关。

  • S1 and S2 are reference bindings (8.5.3) and S1 binds an lvalue reference to a function lvalue and S2 binds an rvalue reference to a function lvalue.

没有。

  • S1 and S2 differ only in their qualification conversion and yield similar types T1 and T2 (4.4), respectively, and the cv-qualification signature of type T1 is a proper subset of the cv-qualification signature of type T2.

资格转换是一个指针的事情,不。

  • S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.

在这种情况下,第一个重载的第二个参数为 double&,而第二个重载的参数为 const double&。前者的 cv 比后者少,所以我们到此为止 - 更喜欢 foo(U&,T&).

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

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,

这就是我们刚刚经历的。

  • the context is an initialization by user-defined conversion [ ... ]
  • the context is an initialization by conversion function for direct reference binding [ ... ]
  • F1 is not a function template specialization and F2 is a function template specialization, or, if not that,
  • F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.

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

请注意,更专业的是最后一个 机制来确定最佳候选者。这是最后的决胜局。

另请注意,数量 的模板推导是无关紧要的。它 可能 在作为模板的重载和非模板的重载之间 select 很重要 - 但在 select 在重载之间无关紧要具有 x 模板参数和具有 y > x 模板参数的重载。