网站建设服务商的网站建设流程,wordpress 创建数据库表,北京市住房和城乡建设局官网,可口可乐公司建设网站的目的是什么意思http://blog.sina.com.cn/s/blog_53b7ddf00101p5t0.htmlstd::move是一个用于提示优化的函数#xff0c;过去的c98中#xff0c;由于无法将作为右值的临时变量从左值当中区别出来#xff0c;所以程序运行时有大量临时变量白白的创建后又立刻销毁#xff0c;其中又尤其是返回…http://blog.sina.com.cn/s/blog_53b7ddf00101p5t0.htmlstd::move是一个用于提示优化的函数过去的c98中由于无法将作为右值的临时变量从左值当中区别出来所以程序运行时有大量临时变量白白的创建后又立刻销毁其中又尤其是返回字符串std::string的函数存在最大的浪费。
比如
1 std::string fileContent “oldContent”;
2 s readFileContent(fileName);因为并不是所有情况下C编译器都能进行返回值优化所以向上面的例子中往往会创建多个字符串。readFileContent如果没有内部状态那么它的返回值多半是std::stringconst std::string的做法不再被推荐了而不是const std::string。这是一个浪费函数的返回值被拷贝到s中后栈上的临时对象就被销毁了。
在C11中编码者可以主动提示编译器readFileContent返回的对象是临时的可以被挪作他用std::move。
将上面的例子改成
1 std::string fileContent “oldContent”;
2 s std::move(readFileContent(fileName));后对象s在被赋值的时候方法std::string::operator (std::string)会被调用符号告诉std::string类的编写者传入的参数是一个临时对象可以挪用其数据于是std::string::operator (std::string)的实现代码中会置空形参同时将原本保存在中形参中的数据移动到自身。
不光是临时变量只要是你认为不再需要的数据都可以考虑用std::move移动。
比较有名的std::move用法是在swap中
1 template
2 void swap(T a, T b)
3 {
4 T t(std::move(a)); // a为空t占有a的初始数据5 a std::move(b); // b为空 a占有b的初始数据6 b std::move(t); // t为空b占有a的初始数据7 } 总之std::move是为性能而生的正式因为了有了这个主动报告废弃物的设施所以C11中的STL性能大幅提升即使C用户仍然按找旧有的方式来编码仍然能因中新版STL等标准库的强化中收益。std::forward是用于模板编程中的如果不需要编写通用的模板类和函数可能不怎么用的上它。
要认识它的作用需要知道C中的几条规则这里有篇挺好的文章http://blog.csdn.net/zwvista/article/details/6848582但似乎因标准的更新其中的规则已不完全成立了
1. 引用折叠规则
X XX XX XX X
2. 对于模板函数中的形参声明T这里的模板参数T最终推演的结果可能不是一个纯类型它可能还会带有引用/常量修饰符如T推演为const int时实际形参为const int 会有如下规则
如果调用函数时的实参为U这里的U可能有const/volatile修饰但没有左/右引用修饰了那么T推演为U显然根据上面的引用折叠规则U U。
如果调用实参为U虽然将T推导为U和U都能满足折叠规则U U且U U但标准规定这里选择将T推演为U而非U。
总结一下第2条规则当形参声明为T时对于实参UT被推演为U当实参是U时T被推演为U。当然T和U具有相同的const/volatile属性。
3.这点很重要也是上面zwvista的文章中没有提到的形参T t中的变量t始终是左值引用即使调用函数的实参是右值引用也不例外。可以这么理解本来左值和右值概念的本质区别就是左值是用户显示声明或分配内存的变量能够直接用变量名访问而右值主要是临时变量。当一个临时变量传入形参为T t的模板函数时T被推演为U参数t所引用的临时变量因为开始能够被据名访问了所以它变成了左值。这也就是std::forward存在的原因当你以为实参是右值所以t也应该是右值时它跟你开了个玩笑它是左值如果你要进一步调用的函数会根据左右值引用性来进行不同操作那么你在将t传给其他函数时应该先用std::forward恢复t的本来引用性恢复的依据是模板参数T的推演结果。虽然t的右值引用行会退化变成左值引用但根据实参的左右引用性不同T会被分别推演为U和U这就是依据因此传给std::forward的两个参数一个都不能少std::forward(t)。再来讨论一下一个模板函数如果要保留参数的左右值引用性为什么应该声明为T
如果声明函数f(T t)实参会直接进行值传递失去了引用性。
如果声明函数f(T t): 根据引用折叠法则无论T是U还是UT的折叠结果都只会是U即这个声明不能用于匹配右值引用实参。
如果声明函数f(T t): 如果T为UT的结果是U可以匹配左值实参如果T为UT的结果是U可以匹配右值实参。又因为T的cv性和U相同所以这种声明能够保留实参的类型信息。先来看一组帮助类
1 template struct TypeName { static const char *get(){ return Type; } };
2 template struct TypeNameSPAN stylePADDING-BOTTOM: 0px; LINE-HEIGHT: 1.5; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; FONT-FAMILY: Courier New; COLOR: rgb(0,0,255); FONT-SIZE: 12px; PADDING-TOP: 0pxconst T { static const char *get(){ return const Type; } };
3 template struct TypeName { static const char *get(){ return Type; } };
4 template struct TypeNameSPAN stylePADDING-BOTTOM: 0px; LINE-HEIGHT: 1.5; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; FONT-FAMILY: Courier New; COLOR: rgb(0,0,255); FONT-SIZE: 12px; PADDING-TOP: 0pxconst T { static const char *get(){ return const Type; } };
5 template struct TypeName { static const char *get(){ return Type; } };
6 template struct TypeNameSPAN stylePADDING-BOTTOM: 0px; LINE-HEIGHT: 1.5; MARGIN: 0px; PADDING-LEFT: 0px; PADDING-RIGHT: 0px; FONT-FAMILY: Courier New; COLOR: rgb(0,0,255); FONT-SIZE: 12px; PADDING-TOP: 0pxconst T { static const char *get(){ return const Type; } };在模板函数内部将模板参数T传给TypeName就可以访问T的类型字符串TypeName::get()。再一个帮助函数用于打印一个表达式的类型
1 template
2 void printValType(T val)
3 {
4 cout TypeName::get() endl;
5 }注意3条规则在这个模板函数上的应用。规则1解释了T val的声明足以保留实参的类型信息。规则2说明了当实参是string时T就是string当实参是const string时T就是const string而非const string。规则3强调无论实参是string还是string形参val的类型都是string!
注意TypeName的写法因为T只能为U或者U显然T可以根据折叠法则还原为实参类型U和U。这里是常见的const/左右引用组合的情形
1 class A{}; // 测试类2 A lRefA() { static A a; return a;} // 左值3 const A clRefA() { static A a; return a;} // 常左值4 A rRefA() { return A(); } // 右值5 const A crRefA() { return A(); } // 常右值测试一下上面的表达式类型
1 printValType(lRefA());
2 printValType(clRefA());
3 printValType(rRefA());
4 printValType(crRefA());输出依次是 Typeconst TypeTypeconst Type。现在正式来探讨std::forward的实现。
回顾一下使用std::forward的原因由于声明为f(T t)的模板函数的形参t会失去右值引用性质所以在将t传给更深层函数前可能会需要回复t的正确引用行当然修改t的引用性办不到但根据t返回另一个引用还是可以的。恰好上面的函数printValType是一个会根据实参类型不同作出不同反映的函数所以可以把它作为f的内层函数来检测f有没有正确的修正t的引用行。1 template2 void f(T a)3 {4 printValType(a);5 }6 7 int main()8 {9 f(lRefA());
10 f(clRefA());
11 f(rRefA());
12 f(crRefA());
13 }输出Typeconst TypeTypeconst Type。
可见后两个输出错了这正是前面规则3描述的当实参是右值引用时虽然T被推演为U但是参数a退化成了左值引用。
直接应用std::forward
1 template
2 void f(T a)
3 {
4 printValType(std::forward(a));
5 }输出Typeconst TypeTypeconst Type。
输出正确了这就是std::forward的作用啊。如果更深层的函数也需要完整的引用信息如这里的printValType那就应该在传递形参前先std::forward在编写自己的forward函数之前先来尝试直接强制转化参数a
1 template
2 void f(T a)
3 {
4 printValType((T)a);
5 }输出Typeconst TypeTypeconst Type。
正确因为不管T被推演为U还是U只要T肯定能还原为U和U。考虑下自己的forward函数应该怎么写因为在forward的调用方中形参已经丢失了右值引用信息唯一的参考依据是T要根据T还原为正确的参数得T因此强制转换和返回类型都是T了当然forward还必须被以forward()的方式显示指定模板类型这样才能保证forward的模板参数T和上层函数f的T是相同类型。首先
1 template
2 T forward(... a)
3 {
4 return (T)a;
5 }调用方f一定得显示指定类型forward。
形参怎么写形参a的类型由T构成而且forward的实参一定是左值暂时不考虑forward(std::string())的使用方法也就是说无论T是U还是U形参a的类型一定都得是U才能和实参匹配所以结果是
1 template
2 T forward(T a)
3 {
4 return (T)a;
5 }测试输出Typeconst TypeTypeconst Type。正确再试下如果f调用forward的时候使用forward(a)的方式没有显示指定模板类型会怎么样
1 template
2 void f(T a)
3 {
4 printValType(forward(a));
5 }输出Tconst TypeTypeconst Type。
错了。分析下因为实参始终是左值所以forward的形参T a中T就被推演为U因此(T)a也就是(U)a所以结果错误。
为了避免用户使用forward(a)因此应该禁用forward的自动模板参数推演功能可以借助std::identity另外将(T)换成static_cast规范一下
1 template
2 T forward(typename std::identity::type a)
3 {
4 return static_cast(a);
5 }上面讲的是针对T为U或U而实参始终为左值的情况这是常见的情形不过也有实参为右值的情况还需要改进上面这个forward但我这里就不写了。
这是我手里的gcc4.5.2的forward实现1 /// forward (as per N2835) 2 /// Forward lvalues as rvalues. 3 template4 inline typename enable_if::value, _Tp::type5 forward(typename std::identity_Tp::type __t)6 { return static_cast_Tp(__t); }7 8 /// Forward rvalues as rvalues. 9 template
10 inline typename enable_if::value, _Tp::type
11 forward(typename std::identity_Tp::type __t)
12 { return static_cast_Tp(__t); }
13
14 // Forward lvalues as lvalues.15 template
16 inline typename enable_if::value, _Tp::type
17 forward(typename std::identity_Tp::type __t)
18 { return __t; }
19
20 // Prevent forwarding rvalues as const lvalues.21 template
22 inline typename enable_if::value, _Tp::type
23 forward(typename std::remove_reference_Tp::type __t) delete;第1/3版本就相当于我之前的实现而版本2/4是实参为右值的情况至于后者这种取舍的原因还得去自己研究下使用场合和文档了。
我手里的vc2010实现的forward和我之前的实现相同显然还不够不过vc2010本来对标准也就还支持得少..