5

我正在阅读有关完美转发的内容,这是我学到的让我感到困惑的事情:
当您尝试实现完美转发时,您将执行以下操作:

template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T &&x);  // like this: void foo(int& &&x)

所以我想,等等,这是否意味着如果我这样做:

template<class T> // If I were to call foo with an l-value int, foo would look
void foo(T x);    // like this: void foo(int& x);

但事实并非如此。foo看起来像这样:void foo(int x);

我的问题:为什么在完美的转发功能中,T 变成了 T& 或 T&&,但在另一个中,T 不是参考?有人能告诉我这个的确切规则吗?我需要澄清一下!

4

4 回答 4

9

模板形参只有出现在函数形参T中才能推导出为引用类型T&&

表单的函数模板:

  • template<class T> void f(T x)
    将推断T为对象类型(并且x是对象类型,因此按值传递)

  • template<class T> void f(T& x)
    将推断T为对象类型(然后x具有左值引用类型)

  • template<class T> void f(T&& x)
    将推断T

    • 左值引用(x由于引用折叠规则而具有左值引用类型
    • 作为对象类型(x右值引用类型也是如此)

完美的转发功能怎么来的,T变成了T&或T&&,[...]

这是错误的。 T成为引用类型L&对象类型R而不是引用R&&
形式的函数参数T&&因此变为

  • 要么L&(因为将右值引用添加到左值引用仍然是左值引用,就像add_rvalue_reference<L&>::type仍然一样L&
  • 或者它变成R&&(因为add_rvalue_reference<R>::typeR&&
于 2013-04-24T13:11:51.623 回答
7

这是因为类型推导的定义方式,它只与完美转发有关,因为std::forward<>()如果传递了右值引用,则结果是右值,如果传递了左值引用,则结果是左值。

但一般来说,当您没有引用开始时,您T不会被推断为引用类型(即 as A&,无论A可能是什么)。如果是这样的话,正如Yakk在评论中正确指出的那样,编写一个按值获取参数的函数模板是不可能的。

特别是,您所指的引用折叠规则在 C++11 标准的第 14.8.2.1/4 段中定义:

如果 P 是引用类型,则使用 P 所引用的类型进行类型推导。如果 P 是对 cv 非限定模板参数的右值引用并且参数是左值,则使用类型“对 A 的左值引用”代替 A 进行类型推导。[示例

template <class T> int f(T&&);
template <class T> int g(const T&&);
int i;
int n1 = f(i); // calls f<int&>(int&)
int n2 = f(0); // calls f<int>(int&&)
int n3 = g(i); // error: would call g<int>(const int&&), which
// would bind an rvalue reference to an lvalue

—结束示例]

于 2013-04-24T13:12:15.170 回答
6

像这样推导模板参数时,需要考虑三种一般情况。

  1. void foo(T x):这意味着“按值传递”。它总是推断出适合按值传递的类型。

  2. void foo(T& x):这意味着“通过左值引用”。它总是推断出适当的类型以通过左值引用传递。

  3. void foo(T&& x):这意味着“通过引用”。它总是推断出适合通过引用传递的类型,它可以是左值引用或右值引用。

于 2013-04-24T13:12:35.730 回答
6

放松,放慢速度,呼吸。

模板参数推导是您需要了解的核心机制,它并非完全无关紧要。当你说template <typename T> void foo(T), thenT总是被推断为非引用类型。

如果你想要一个引用,你必须&在它上面加上一个:template <typename T> void foo(T&)也会推断T为一个非引用类型,但foo现在总是需要一个左值引用。

最后一块魔法来自新的参考折叠规则。当您说 时template <typename T> void foo(T&&),可能会发生两件事:

  • foo使用右值调用,例如foo(Bar()). 然后T推导出为Bar,并foo采用对 的右值引用Bar,即 a Bar&&

  • foo使用左值调用,例如Bar x; foo(x);. 现在唯一foo可以采用的是左值引用。由于折叠规则,这需要T推断为Bar&,因为。T&& == Bar& && == Bar&

只有这个最终模板能够同时接受左值和右值。这就是为什么它有时被称为“通用参考”的原因;但请记住,重要的不是引用,而是模板参数推导。Usingstd::forward<T>允许您传递具有与您收到的相同值类别的参数。

于 2013-04-24T13:14:06.163 回答