C++primer学习:模板编程(6):模板实参推断和forward的运用与源代码

下面讨论一下当函数参数是引用时的情况.

(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));
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值