翻译「C++ Rvalue References Explained」C++右值引用详解 Part4:强制Move语义
本文为第四部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/4220233.html。
强制Move语义
众所周知,正如C++标准的第一修正案所陈述:“委员会不会建立任何试图绊住C++程序员的脚的规则。(The committee shall make no rule that prevents C++ programmers from shooting themselves in the foot.)”,正经来说,就是当面临给予程序员更多控制还是减少他们粗心大意机会的选择时,C++更倾向于及时可能导致犯错,但是依然给予更多控制。正是基于这种精神,C++11允许你使用Move语义而不仅仅局限于是右值,而是还有左值,这都给与你充分的决定权。一个好的例子就是标准库里面的swap方法。同以前一样,给出一个类X,基于它我们可以重载拷贝构造函数和拷贝赋值操作符来在右值上面实现Move语义。
template<class T>
void swap(T& a, T& b)
{
T tmp(a);
a = b;
b = tmp;
} X a, b;
swap(a, b);
这里并没有右值。所以,在swap函数中的三行没有使用move语义。但是我们知道使用move语义是可行的:任何时候当一个变量作为源头出现在一个拷贝构造函数或者赋值语句的时候,那个变量将不会再被使用,或者仅仅被作为赋值的目标。
在C++11中,标准库中有一个叫做std::move的方法用来处理这种情况。这个函数只是将他的参数变成右值。因此,在C++11中,标准库函数swap将会像如下所示:
template<class T>
void swap(T& a, T& b)
{
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
} X a, b;
swap(a, b);
现在所有在swap函数中的三行都使用了move语义。记住对于没有实现move语义的类型(也就是说,并没有为右值单独重载一个拷贝构造函数和赋值表达式的类型),新的swap行为表现同旧的版本是一致的。
std::move是一个非常简单的函数。不幸的是,尽管如此,我还不能将它的实现展现给你。我们将会在后面讨论它。
在任何能使用std::move的地方使用它。如上面的swap函数所示,它给我们带来如下好处:
- 对于实现了move语义的类型而言,需要标准库算法和操作将会使用move语义,然后因此得到潜在的性能提升。一个重要的例子就是原地排序(inplace sorting):原地排序算法就是单纯的交换元素,其他什么都不做,然后现在交换的过程将会在提供了move语义的类型中,充分利用到move语义提供的优势。
- 标准模板库(STL)经常需要有些类型提供拷贝能力(copyability)。例如可以被用做容器元素的类型。通过严格的观察,事实证明,在很多情况下,移动能力(moveability)就足够了。因此,我们现在可以在某些以前并不被允许的地方使用可移动的而不是可复制的类型了(譬如
unique_pointer)。举例来说,这些类型现在可以被使用做标准模板库的容器类型了。
既然我们知道了std::move,我就就需要知道为什么实现一个基于右值引用的拷贝赋值表达式的重载,如同前面我所展示的,依然是有些问题的。考虑一个简单的变量间的赋值,像这样:
a = b;
你期待在这里发生什么?你期待被a持有的对象被一份b的拷贝所替代,当然在整个交换过程中,你期待原来被a持有的对象会被析构。现在考虑这行代码:
a = std::move(b);
如果move语义被实现为一个简单的交换,那么这里的表现就将会是被a和b持有的对象将在a和b间交换。没有任何东西被析构。当然原来被a持有的对象将会最终被析构,也就是说,当b离开了该代码的范围时。当然,除非b成为move的对象,在这种情况下,原来被a持有的对象又再次得到了一次。因此,只有拷贝赋值表达式的实现被精心考虑过后,我们并不知道原来被a持有的对象何时将被析构。
所以在某种意义上,我们在这里将会陷入到不确定性的泥沼中:一个变量被分配后,但是原来被该变量持有的对象却还在其他位置。只有那个对象的析构函数并不会对外面有任何副作用的时候才是可行的。但是某些时候,析构函数确实会有这种副作用。一个例子就是在析构函数中释放一个锁。因此,析构函数中任何可能含有副作用的部分应该在拷贝赋值操作符的右值引用重载中清晰地表现出来:
X& X::operator=(X&& rhs)
{ // 执行一次清理,要注意到那些在析构函数中可能产生副作用的地方。
// 确保对象处于可析构的和可赋值的状态 // Move语义,交换this和rhs的内容 return *this;
}
翻译「C++ Rvalue References Explained」C++右值引用详解 Part4:强制Move语义的更多相关文章
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part6:Move语义和编译器优化
本文为第六部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part1:概述
本文系对「C++ Rvalue References Explained」 该文的翻译,原文作者:Thomas Becker. 该文较详细的解释了C++11右值引用的作用和出现的意义,也同时被Scot ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part8:Perfect Forwarding(完美转发):解决方案
本文为第八部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part3:右值引用
本文为第三部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/4220233.html. 右值引用 如果x是任意类型,那么x&&则被称作一个 ...
- 翻译「C++ Rvalue References Explained」C++右值引用详解 Part5:右值引用就是右值吗?
本文为第五部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...
- 「翻译」Unity中的AssetBundle详解(二)
为AssetBundles准备资源 使用AssetBundles时,您可以随意将任何Asset分配给所需的任何Bundle.但是,在设置Bundles时,需要考虑一些策略.这些分组策略可以使用到任何你 ...
- 「翻译」Unity中的AssetBundle详解(一)
AssetBundles AssetBundle是一个存档文件,其中包含平台在运行时加载的特定资产(模型,纹理,预制,音频剪辑,甚至整个场景).AssetBundles可以表示彼此之间的依赖关系;例如 ...
- C++11标准之右值引用(rvalue reference)
1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了Copy Elision.RVO(包 ...
- c++11-17 模板核心知识(十)—— 区分万能引用(universal references)和右值引用
引子 如何区分 模板参数 const disqualify universal reference auto声明 引子 T&&在代码里并不总是右值引用: void f(Widget&a ...
随机推荐
- LeetCode "Longest Substring with At Most K Distinct Characters"
A simple variation to "Longest Substring with At Most Two Distinct Characters". A typical ...
- android学习笔记17——对话框(PopupWindow)
PopupWindow ==> PopupWindow可创建类似对话框的窗口,使用其创建对话框窗口的操作步骤: 1.调用PopupWindow构造器构造PopupWindow对象: 2.调用Po ...
- Mysql分区技术
注:分区的语法可以看手册中有详细的写法和例子: show plugins; 此命令查看可有partition这个选项,有则mysql支持分区,没有的话,就可以升级一下mysql 实时监控一个命令执行情 ...
- PHP 正则表达式常用函数使用小结
在PHP中有两套正则表达式函数库.一套是由PCRE(Perl Compatible Regular Expression)库提供的.PCRE库使用和Perl相同的语法规则实现了正则表达式的模式匹配,其 ...
- Servlet细节
Servlet细节 线程不安全的做法: * 不要在Servlet中创建成员!创建局部变量即可! * 可以创建无状态成员! * 可以创建有状态的成员,但状态必须为只读的!(不提供set方法) 1.Ser ...
- OAF_VO系列1 - Accelerator Keys
OAF_EO系列6 - Delete详解和实现(案例) (2014-06-16 08:37)
- HDU 2176 取(m堆)石子游戏(Nim)
取(m堆)石子游戏 题意: Problem Description m堆石子,两人轮流取.只能在1堆中取.取完者胜.先取者负输出No.先取者胜输出Yes,然后输出怎样取子.例如5堆 5,7,8,9,1 ...
- 0808HTML
打开DREAMWEAVER,新建HTML,页面上有几句代码 <html>开始标签 <head>网页上的控制信息 <title>页面标题</title> ...
- 斑马打印机网卡ZebraNet配置(有线)
实图: 抽象图: 说明: 1.并口,用于连接斑马打印机一端 2.网络连接状态指示灯 3.打印状态指示灯 4.测试按钮,在连接打印机的情况下,按下此键,会打印出网卡信息. 5.网线接口 按下测试按钮之后 ...
- Spring中IOC和AOP的详细解释
我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂. 主要用到的设计模式有工厂模式和代理模式. IOC就是典型的工厂模式,通过s ...