本文为第四部分,目录请参阅概述部分: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语义的更多相关文章

  1. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part6:Move语义和编译器优化

    本文为第六部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...

  2. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part1:概述

    本文系对「C++ Rvalue References Explained」 该文的翻译,原文作者:Thomas Becker. 该文较详细的解释了C++11右值引用的作用和出现的意义,也同时被Scot ...

  3. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part8:Perfect Forwarding(完美转发):解决方案

    本文为第八部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...

  4. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part3:右值引用

    本文为第三部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/4220233.html. 右值引用 如果x是任意类型,那么x&&则被称作一个 ...

  5. 翻译「C++ Rvalue References Explained」C++右值引用详解 Part5:右值引用就是右值吗?

    本文为第五部分,目录请参阅概述部分:http://www.cnblogs.com/harrywong/p/cpp-rvalue-references-explained-introduction.ht ...

  6. 「翻译」Unity中的AssetBundle详解(二)

    为AssetBundles准备资源 使用AssetBundles时,您可以随意将任何Asset分配给所需的任何Bundle.但是,在设置Bundles时,需要考虑一些策略.这些分组策略可以使用到任何你 ...

  7. 「翻译」Unity中的AssetBundle详解(一)

    AssetBundles AssetBundle是一个存档文件,其中包含平台在运行时加载的特定资产(模型,纹理,预制,音频剪辑,甚至整个场景).AssetBundles可以表示彼此之间的依赖关系;例如 ...

  8. C++11标准之右值引用(rvalue reference)

    1.右值引用引入的背景 临时对象的产生和拷贝所带来的效率折损,一直是C++所为人诟病的问题.但是C++标准允许编译器对于临时对象的产生具有完全的自由度,从而发展出了Copy Elision.RVO(包 ...

  9. c++11-17 模板核心知识(十)—— 区分万能引用(universal references)和右值引用

    引子 如何区分 模板参数 const disqualify universal reference auto声明 引子 T&&在代码里并不总是右值引用: void f(Widget&a ...

随机推荐

  1. 【1-4】jQuery代码风格-导航栏

    实现一个导航栏,单机不同的商品名称链接,显示相应的内容,同时高亮显示当前选择的商品. 实现功能如图: css: /* reset */ ;padding:0 0 12px 0;font-size:12 ...

  2. WinForm窗体拖动代码

    本文转载自:http://www.cnblogs.com/ap0606122/archive/2012/10/23/2734964.html using System; using System.Co ...

  3. bzoj1015 星球大战

    Description 很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治者整个星系.某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球.这些星球通过 ...

  4. php定时执行脚本

    php定时执行脚本 ignore_user_abort(); // run script. in background set_time_limit(0); // run script. foreve ...

  5. "aa".equals(name)这种写法为什么就可以避免空指针

    public static void main(String[] args) { String name=null; if("aa".equals(name))//这种情形,不出现 ...

  6. eclipse导入html、js、xml报错的问题

    今天重新安装eclipse,在导入部分html.js.xml文件,报错,解决办法如下: eclipse->window->preferences->Team,点击validation ...

  7. SQL Server 格式化时间 之 format函数

    select format(getdate(),'yyyy-MM-dd HH:mm:ss'); 要注意 yyyy-MM-dd这里MM是大写的, HH:mm:ss这里HH是大写的,mm是小写的,大小写意 ...

  8. java中的浅拷贝与深拷贝

    浅拷贝: package test; class Student implements Cloneable { private int number; public int getNumber() { ...

  9. Android之SQLite

    在模拟器运行的情况下,进入cmd运行adb shell 可进入模拟器的linux系统输入 lite3 mars_test_db 可进入sqlite模式.schema或者.sch 查看有哪些表 SQLi ...

  10. 漫谈刑事辩护 z

    各位律师,各位助理: 大家好!今天的律师沙龙由我来给大家谈一谈刑事辩护方面的问题.这次我谈的,主要是我这么多年来办理刑事案件,从事刑事辩护中的一些体会. 刑事辩护,大家最关心的莫过于收费问题了.我认为 ...