本次要讲的是右值引用相关的几个函数:std::move, std::forward和成员的emplace_back,通过这些函数我们可以避免不必要的拷贝,提高程序性能。move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。如图所示是深拷贝和move的区别。
A& A::operator=(const A& rhs) { // 销毁m_ptr指向的资源 // 复制rhs.m_ptr所指的资源,并使m_ptr指向它 } 同样A的拷贝构造函数也是这样。假设我们这样来用A: A foo(); // foo是一个返回值为X的函数 A a; a = foo(); 最后一行有如下的操作:
上面的过程是可行的,但是更有效率的办法是直接交换a和临时对象中的资源指针,然后让临时对象的析构函数去销毁a原来拥有的资源。换句话说,当赋值操作符的右边是右值的时候,我们希望赋值操作符被定义成下面这样: A& A::operator=(const A&& rhs) { // 仅仅转移资源的所有者,将资源的拥有者改为被赋值者 } 这就是所谓的move语义。再看一个例子,假设一个临时容器很大,赋值给另一个容器。 { std::list< std::string > tokens;//省略初始化... std::list< std::string > t = tokens; } std::list< std::string > tokens; std::list< std::string > t = std::move(tokens); 如果不用std::move,拷贝的代价很大,性能较低。使用move几乎没有任何代价,只是转换了资源的所有权。如果一个对象内部有较大的对内存或者动态数组时,很有必要写move语义的拷贝构造函数和赋值函数,避免无谓的深拷贝,以提高性能。 完美转发在上一篇的博文中我介绍了右值引用,右值引用类型是独立于值的,一个右值引用参数作为函数的形参,在函数内部再转发该参数的时候它已经变成一个左值了,并不是它原来的类型了。因此,我们需要一种方法能按照参数原来的类型转发到另一个函数,这种转发被称为完美转发。所谓完美转发(perfect forwarding),是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。c++11中提供了这样的一个函数std::forward,它是为转发而生的,它会按照参数本来的类型来转发出去,不管参数类型是T&&这种未定的引用类型还是明确的左值引用或者右值引用。看看这个例子。 template<typename T> void PrintT(T& t) { cout << "lvaue" << endl; } template<typename T> void PrintT(T && t) { cout << "rvalue" << endl; } template<typename T> void TestForward(T && v) { PrintT(v); PrintT(std::forward<T>(v)); PrintT(std::move(v)); } Test() { TestForward(1); int x = 1; TestForward(x); TestForward(std::forward<int>(x)); } 测试结果:
万能的函数包装器右值引用、完美转发再结合可变模板参数,我们可以写一个万能的函数包装器,它可以接收所有的函数,带返回值的、不带返回值的、带参数的和不带参数的函数都可以委托这个万能的函数包装器执行。看看这个万能的函数包装器。 template<class Function, class... Args> inline auto FuncWrapper(Function && f, Args && ... args) -> decltype(f(std::forward<Args>(args)...)) { //typedef decltype(f(std::forward<Args>(args)...)) ReturnType; return f(std::forward<Args>(args)...); //your code; you can use the above typedef. } 再看看测试代码: void test0() { cout << "void" << endl; } int test1() { return 1; } int test2(int x) { return x; } string test3(string s1, string s2) { return s1 + s2; } test() { FuncWrapper(test0); //没有返回值,打印1 FuncWrapper(test1); //返回1 FuncWrapper(test2, 1); //返回1 FuncWrapper(test3, "aa", "bb"); //返回"aabb" } 成员的emplace_backc++11中大部分容器都加了一个emplace_back成员函数,vector中它的定义是这样的: template< class... Args > void emplace_back( Args&&... args ); 这里的Args&&是一个未定的引用类型,因此它可以接收左值引用和右值引用,它的内部也是调用了std::forward实现完美转发的。因此如果我们需要往容器中添加右值、临时变量时,用emplace_back可以提高性能。 |
C++11改进我们的程序之move和完美转发
时间: 2024-10-08 07:51:39
C++11改进我们的程序之move和完美转发的相关文章
c++11改进我们的程序之垃圾回收(一)
c#和java中有自己主动垃圾回收机制,.net执行时和java虚拟机能够管理分配的堆内存,在对象失去引用时自己主动回收,因此在c#和jva中, 内存管理不是大问题.c++语言没有垃圾回收机制,必须自己去释放分配的堆内存,否则就会内存泄露. 我相信大部分c++开发者都遇到过内存泄露的问题,而查找内存泄露的问题往往要花大量的精力.要解决这个让人头疼的问题可 以採取一些办法,最有效的办法是使用智能指针!使用智能指针就不会操心内存泄露的问题了,由于智能指针能够自己主动删除删除分 配的内存. 智能指
c++11 标准库函数 std::move 和 完美转发 std::forward
c++11 标准库函数 std::move 和 完美转发 std::forward #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <vector> #include <map> // C++中还有一个被广泛认同的说法,那就是可以取地址的.有名字的就是左值,反之,不能取地址的.没有名字的就是右值. // 相对于左值,右值表示字面常量.表达式.函数的非
[c++11]右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象.所有的具名变量或者对象都是左值,而右值不具名.很难得到左值和右值的真正定义,但是有一个可以区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值. 看见书上又将右值分为:将亡值和纯右值. 纯右值就是c++98标准中右值的概
[转载]如何在C++03中模拟C++11的右值引用std::move特性
本文摘自: http://adamcavendish.is-programmer.com/posts/38190.htm 引言 众所周知,C++11 的新特性中有一个非常重要的特性,那就是 rvalue reference,右值引用. 引入它的一个非常重要的原因是因为在 C++ 中,常常右值,通俗地讲"在等号右边的"临时变量或者临时对象,我们是无法得到它的修改权限的. 由于类的构造和析构机制,往往产生的临时变量或临时对象的拷贝构造及析构,会带来不少的时间.资源消耗. 也同样由于这样的限
[转][c++11]我理解的右值引用、移动语义和完美转发
c++中引入了右值引用和移动语义,可以避免无谓的复制,提高程序性能.有点难理解,于是花时间整理一下自己的理解. 左值.右值 C++中所有的值都必然属于左值.右值二者之一.左值是指表达式结束后依然存在的持久化对象,右值是指表达式结束时就不再存在的临时对象.所有的具名变量或者对象都是左值,而右值不具名.很难得到左值和右值的真正定义,但是有一个可以区分左值和右值的便捷方法:看能不能对表达式取地址,如果能,则为左值,否则为右值. 看见书上又将右值分为将亡值和纯右值.纯右值就是c++98标准中右值的概念,
深入右值引用,move语义和完美转发
深入右值引用,move语义和完美转发 转载请注明:http://blog.csdn.net/booirror/article/details/45057689 乍看起来,move语义使得你可以用廉价的move赋值替代昂贵的copy赋值,完美转发使得你可以将传来的任意参数转发给 其他函数,而右值引用使得move语义和完美转发成为可能.然而,慢慢地你发现这不那么简单,你发现std::move并没有move任何东西,完美转发也并不完美,而T&&也不一定就是右值引用-- move语义 最原始的左值
C++11新特性之 std::forward(完美转发)(转)
我们也要时刻清醒,有时候右值会转为左值,左值会转为右值. (也许"转换"二字用的不是很准确) 如果我们要避免这种转换呢? 我们需要一种方法能按照参数原来的类型转发到另一个函数中,这才完美,我们称之为完美转发. std::forward就可以保存参数的左值或右值特性. 因为是这样描述的: When used according to the following recipe in a function template, forwards the argument to another
c++11 函数内部返回对象使用move语义的最佳实践
一句话,直接返回即可,不用任何变化. 当启动了c++11选项后,通过函数返回代码没有发生任何变化,但是已经使用了move语义,而不需要之前的NRVO编译器优化技术. 注意,右值引用rvalue reference是表达式计算完成后就不再存在的临时变量,左值是表达式计算完成后的变量.如果能够用&求址的,就是左值. 下面是stackoverflow上的一个讨论贴,比较有价值: 246down voteaccepted First example std::vector<int> retur
C++ 11 右值引用以及std::move
转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,int&则是整数引用类型.相同int&&也是一个类型.两个引號&&是C++ 11提出的一个新的引用类型.记住,这是一个新的类型.默念10次吧.假设你记住这个新类型,那么非常多疑问都能迎刃而解.而且对<Effective Modern C++>说到的void f(