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>
允许您传递具有您收到的相同值类别的参数.