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不是参考?有人可以告诉我这个的确切规则吗?我需要一些澄清!
仅当模板参数出现在表单的函数参数中时,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>::type是R&&)这是因为定义了类型推导的方式,并且它仅与完美转发相关,在这种意义上,std::forward<>()如果传递了右值引用,则结果为rvalue;如果传递了左值引用,则结果为左值.
但一般来说,当你没有开头的引用时,你T不会被推断为引用类型(即A&,无论什么A可能).如果是这种情况,正如Yakk在评论中正确指出的那样,就不可能编写一个按值来获取其参数的函数模板.
特别是,您引用的引用折叠规则在C++ 11标准的第14.8.2.1/4节中定义:
如果P是引用类型,则P引用的类型用于类型推导.如果P是对cv-nonqualified模板参数的rvalue引用,并且参数是左值,则使用类型"对A的左值引用"代替A来进行类型推导.[ 例如:
Run Code Online (Sandbox Code Playgroud)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- 末端的例子 ]
在推导出像这样的模板参数时,有三种一般情况需要考虑.
void foo(T x):这意味着"按值传递".它总是推导出适合传递值的类型.
void foo(T& x):这意味着"通过左值引用".它总是推导出适合传递左值引用的类型.
void foo(T&& x):这意味着"传递参考".它总是推导出一种适合通过引用传递的类型,它可以是左值引用或右值引用.
放松,减速和呼吸.
模板参数推导是您需要理解的核心机制,并不是完全无关紧要的.当你说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>允许您传递具有您收到的相同值类别的参数.