下面讨论一下当函数参数是引用时的情况.
(1)左值引用 T&,只能传递给它一个左值.实参的const属性会被保留.(注意这里不是值拷贝,而是引用,const是底层的,不会被忽略).而T会被推断为实参引用的类型;
(2)右值引用T&&,正常规则是传递给它一个右值,这时候会将T推断为右值实参的类型.如果传递的是一个左值,那么就会发生特殊的情况.首先T会被推断为实参的左值引用类型.然后形参则相当于X& &&类型.这里引入C++的引用折叠规则,既X& &&,X& &,X&& &都折叠成类型X&.而X&& &&则折叠成类型X&&.
template <typename T>
void g(T&& val)
{
vector<T> v;
}
const int ci = 1;
int i = 2;
(a) g(i);// T被推断为int&,val:int&
(b) g(ci);//T:const int &&,val:const int &
(c) g(i * ci);//T:int,val:int&&
======================================================================================
转发:某些函数需要将其一个或多个实参连同类型不变d转发给其他函数,在这种情况下,我们需要保持实参的所有性质,包括其是引用,const,左值或者右值.
首先我们定义一个函数叫做flip()的函数.
[1]首先为了保证引用的属性不被破坏,我们必须定义为引用的形式.因为如果外层函数不是引用类型,那么函数f如果接受引用的形参,就会将其绑定到flip的形参上面.这是没有意义的.它不能改变真正的实参.
[2]其次外层的形参应该是右值引用的形式.这是因为如果使用左值引用,当我们传入一个右值引用的时候,它会变成左值的引用.这样如果需要通过左右值来区分就会出问题.
[3]尽管如此,还有一些问题没有解决.假如内层的函数需要接受一个右值,那么无论外层实参是什么都会编译出错.这是因为一个右值引用在具有名字的情况下也是一个左值.我们可以通过flip的形参调用它,那么它就是一个左值.这样f的形参会绑定在左值上,这在非模板函数里面是不允许的.
[4]这时候我们就需要一个新的标准库函数,std::forward<T>(u)
,他可以在保存原来性质的前提下返回实参的真正属性.
void f(int &&v1, int &v2)
{
cout << v1 <<endl<< ++v2 ;
}
template<typename F, typename T1,typename T2>
void flip(F f,T1&& t1, T2&& t2)
{
f(t2, t1);
f(std::forward<T2>(t2), std::forward<T1>(t1));
}
======================================================================================
下面是forward的源代码.我们来仔细分析下它的机制.
[1]它是一个函数模板,模板参数是_Ty.形参类型是typename remove_reference<_Ty>::type&.remove_ference是为了确保所得的类型一定是左值引用.否则可能发生引用折叠.
[2]返回了一个static_cast<_Ty&&>(_Arg)。这是一个强制类型转换,不会影响const的属性.而且返回类型是_Ty&& _Arg.如果_Ty是左值引用(那么flip的实参就一定是左值),那么根据折叠规则,返回一个左值引用.一个不具名的左值引用还是左值.这不会改变什么.反之,得到一个不具名的右值引用,它因为没有名字,所以是右值.这样就得到了一个属性为右值的右值引用.成功恢复了原来的属性.
======================================================================================
对于第二个重载的模板,主要是对右值进行转化.但是这不是一个很好的选择.因为右值的属性是不需要调用这个函数了.一般来说调用它是程序设计的问题.
template<class _Ty> inline
_Ty&& forward(typename remove_reference<_Ty>::type& _Arg)
{ // forward an lvalue
return (static_cast<_Ty&&>(_Arg));
}
template<class _Ty> inline
_Ty&& forward(typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{ // forward anything
static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}