模板实参推断和引用以及move和forward详解

来源:互联网 发布:淘宝网络电视机顶盒 编辑:程序博客网 时间:2024/05/18 03:31

模板实参推断和引用以及move和forward详解

从左值引用函数参数推断类型

当一个函数的参数是一个左值引用时,我们只能传递给它一个左值,实参可以是const类型或非const类型,实参是const,则T被推断为const。
例如:

template<typename T> void f1(T&);f1(i);  //实参为int,则T推断为intf1(ci); //实参为const int,T推断为const intf1(5);  //实参为右值,不能传入右值,错误

如果一个函数参数类型是const T&,正常绑定规则告诉我们可以给它传递任何类型实参–一个对象(const或非const)、一个临时对象或是一个字面常量值。当函数参数本身是const时,T的类型推断的结果不会是一个const类型,因为const已经是函数参数类型的一部分了,它不会再是模板参数类型的一部分。

template<typename T> void f2(const T&);f2(i);  //i是int,则模板参数类型推断为intf2(ci); //i是const int,模板参数同样被推断为intf2(5);  //一个const &参数可以绑定到右值,则T为int

从右值引用函数参数推断类型

当一个函数参数是一个右值引用时,显然我们可以传递一个右值给它。

template<typename T> void f3(T&&);f3(42);  //实参是一个int型右值,模板参数类型为int

引用折叠和右值引用参数

我们同样可以将一个左值传给一个参数为右值引用的模板函数,此时编译器会推断模板类型参数为实参的左值引用类型,例如当我们调用f3(i)时,此时T被推断为int &,而非int。
此时就会出现引用的引用这种情况,这种情况下,这些引用会形成“折叠”,折叠成左值的引用或者右值的引用。
* X& &、X& &&、X&& &都被折叠成X&
* 类型X&& &&被折叠成X&&

f3(i);  //实参是一个左值,模板参数T推断为int&,则参数i类型变为int& &&,由引用折叠得到为int&f3(ci); //实参是一个左值,模板参数T推断为const int&

编写接受右值引用参数的模板参数

template<typename T> void f3(T &&val){  T t = val;}
  • 当我们对一个右值调用f3时,例如f3(43),T被推断为int,此时,局部变量t是int型,是val的一个拷贝。
  • 当我们对一个左值调用f3时,T被推断为int &,此时t的类型是int&,t不是val的拷贝,而是val的一个引用。

move

标准库是这样定义move的:

template<typename t>typename remove_reference<T>::type&& move(T&& t){  return static_cast<typename remove_reference<T>::type&&>(t);}

当调用std::move(string(“bye”))时,实参为一个右值:

* 推断出T类型为string* remove_reference<string>::typestring* 将t强制转换为string&&并返回。

当调用std::move(s)时,s为一个左值:

* 推断出T类型为string&* 则t为string& &&,折叠成string&* remove_reference<string&>::typestring* 转换t为string&&,并返回

forward

除了move()语义之外,右值引用还需要解决的一个问题就是完美转发,转发问题针对的是模板函数,要求我们保持实参类型将其传递给其他函数。
forward原型类似如下:

template<class TYPE>TYPE&& forward(typename remove_reference<TYPE>::type& arg){  return static_cast<TYPE&&>(arg);}

通常情况下,我们使用forward传递那些定义为模板类型参数的右值引用的函数参数。通过其返回类型上的引用折叠,forward可以保持给定实参的左值/右值属性:

template<typename Type> intermediary(Type &&arg){  finalFcn(std::forward<Type>(arg));}当传入一个右值参数例如string("ss"):* Type会被推断为string,调用forward<string>* forward的返回类型TYPE&&string&&* arg为remove_reference<string>::type&也就是string&类型* 再将arg强制为string&&类型返回,保持了arg的右值属性。当传入一个左值参数,例如string时:* Type推断为string&,则arg为string& &&类型,引用折叠得到string&类型。此时调用forward<string&>* forward返回值为string& &&,折叠为string&* arg为remove_reference<string&>::type&string&类型* 将arg转为string&返回,保持了arg的左值属性。
原创粉丝点击