C++ 入门模板通用参考和参数推导

C++ primer template universal reference and argument deduction

你好,我有这个来自 C++ primer 的例子:

 template <typename T>
 void f(T&& x) // binds to nonconstant rvalues
 {
     std::cout << "f(T&&)\n";
 }

 template <typename T>
 void f(T const& x) // lvalues and constant revalues
 {
     std::cout << "f(T const&)\n";
 }

这是我测试输出的尝试:

int main(){

    int i = 5;
    int const ci = 10;

    f(i); // f(T& &&) -> f(int&)
    f(ci); // f(T const&) -> f(int const&)
    f(5); // f(T &&) -> f(int&&)
    f(std::move(ci)); // f(T&&) -> f(int const&&)

    cout << '\n';
}

输出:

f(T&&)
f(T const&)
f(T&&)
f(T&&)

书中的评论显然不完全正确。当您有

的两个可用重载时
template <typename T> void f(T&& x);
template <typename T> void f(T const& x);

它们都可以用任何参数调用(除了一些例外,我将在这里省略),但如果参数是 首选 第二个 =14=]左值。由于推导模板参数时适用的引用折叠规则,第一个在所有其他情况下都是首选。

但是,假设 T 由封闭的 class:

固定
template <class T>
struct S {
    void f(T&& x);
    void f(T const& x);
};

并假设 T 是非 const 对象类型。现在,情况不同了,因为调用 f 时没有推导 T。书中的 评论 似乎指的是后一种情况,其中第一个 S::f 现在可以用 [=15] 类型的非 const 右值调用=] 但不是 const 类型 T 的右值,也不是(可能 constT 的左值。不幸的是,代码与评论不符。

让我们回到书中的实际代码。正如我所说,当参数是 const 左值时,采用 T const& 的函数将是首选。假设参数是 5,但您想要显式调用第二个函数,即使它通常不会被选中。有几种方法可以做到这一点。最容易阅读的是将参数实际转换为 const 左值:

f((const int&)5);

您对 f<const int&>(5) 的建议也有效,但有点令人困惑。之所以令人困惑,是因为它需要代码的 reader 来实际执行精神上的引用折叠,然后记住第一个重载不如第二个重载专门化。第三种方法是:

static_cast<void(*)(int const&)>(&f)(5);

虽然这部分是最难读的,但只要需要提取一个特定的重载并将其绑定到函数指针,就可以使用 (5) 之前的部分。