当你完美前进时,typename T变为T&或T &&,但是当你不完美时,T根本就不是参考.怎么样?

Aar*_*ron 5 c++ templates c++11

我正在阅读关于完美转发的内容,这是我所学到的让我困惑的事情:
当你试图实现完美的转发时,你会做这样的事情:

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)
Run Code Online (Sandbox Code Playgroud)

那么我想,等等,这是否意味着如果我这样做:

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);
Run Code Online (Sandbox Code Playgroud)

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

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

Jon*_*ely 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&&)


And*_*owl 7

这是因为定义了类型推导的方式,并且它仅与完美转发相关,在这种意义上,std::forward<>()如果传递了右值引用,则结果为rvalue;如果传递了左值引用,则结果为左值.

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

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

如果P是引用类型,则P引用的类型用于类型推导.如果P是对cv-nonqualified模板参数的rvalue引用,并且参数是左值,则使用类型"对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
Run Code Online (Sandbox Code Playgroud)

- 末端的例子 ]


R. *_*des 6

在推导出像这样的模板参数时,有三种一般情况需要考虑.

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

  2. void foo(T& x):这意味着"通过左值引用".它总是推导出适合传递左值引用的类型.

  3. void foo(T&& x):这意味着"传递参考".它总是推导出一种适合通过引用传递的类型,它可以是左值引用或右值引用.


Ker*_* SB 6

放松,减速和呼吸.

模板参数推导是您需要理解的核心机制,并不是完全无关紧要的.当你说template <typename T> void foo(T),那T永远推导出非引用类型.

如果你想要一个引用,你必须加上&它: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&

只有这个最终模板才能接受左值和左值.这就是为什么它有时被称为"通用参考"; 但请记住,重要的不是参考,而是模板参数演绎.使用std::forward<T>允许您传递具有您收到的相同值类别的参数.