【问题标题】:C++ primer template universal reference and argument deductionC++入门模板通用引用和参数推导
【发布时间】:2021-12-08 14:06:57
【问题描述】:

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

 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&&)
  • 在书中,f 的版本被称为转发引用只绑定到非常量右值,但在main 中,我传递了一个常量右值f(5)f(std::move(ci)),但仍然是第一个版本调用。

  • 有没有办法调用第二个版本f(T const&amp;) 传递一个右值?我知道我可以明确地做到这一点:f&lt;int const&amp;&gt;(5);f&lt;int const&amp;&gt;( std::move(ci) ); 但我想知道在哪里可以调用传入右值?谢谢!

  • 我认为f 的转发参考版本之后的注释不正确:f(T&amp;&amp;) // binds to nonconstant rvalues 因为它可以绑定到常量右值,例如:

      f(std::move(ci)); // f(int const&&) and not
      f(int const&)
    

【问题讨论】:

  • 我开始觉得这本入门书不应该再出现在推荐书单上了……

标签: c++ template-argument-deduction


【解决方案1】:

显然,书中的 cmets 并不完全正确。当你有两个可用的重载时

template <typename T> void f(T&& x);
template <typename T> void f(T const& x);

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

但是,假设T 由封闭类固定:

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

让我们假设T 是一个非const 对象类型。现在,情况不同了,因为在调用f 时没有推断出T。书中的 cmets 似乎是指后一种情况,其中第一个 S::f 现在可以用非const 类型为T 的右值调用,但不能用const 右值调用类型为T,也不是左值(可能为constT。不幸的是,该代码与 cmets 不匹配。

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

f((const int&)5);

您对f&lt;const int&amp;&gt;(5) 的建议也有效,但有点令人困惑。它之所以令人困惑,是因为它要求代码的读者在心理上实际做引用折叠,然后记住第一个重载没有第二个重载那么专业。第三种方法是:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    • 1970-01-01
    • 2021-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多