[cpp]右值和移动

大神翻译

  • 地址: https://github.com/xiaoweiChen/Cpp_Concurrency_In_Action
  • 转载
  • move是数据转移的优化,避免对将要销毁的变量进行额外的 拷贝。

move的作用

  • 移动语义在线程库中用的比较广泛,无拷贝操作对数据进行转移可以作为一种优化方式,避 免对将要被销毁的变量进行额外的拷贝。在2.2节中看到,在线程中使用 std::move() 转
    移 std::unique_ptr<> 得到一个新实例;在2.3节中,了解了在 std:thread 的实例间使用移动 语义,用来转移线程的所有权。
  • std::thread 、 std::unique_lock<> 、 std::future<> 、
    std::promise<> 和 std::packaged_task<> 都不能拷贝,不过这些类都有移动构造函数,能让相 关资源在实例中进行传递,并且支持用一个函数将值进行返
    回。
  • std::string 和 std::vector<> 也可以拷贝,不过它们也有移动构造函数和移动赋值操作 符,就是为了避免拷贝拷贝大量数据。
  • C++标准库不会将一个对象显式的转移到另一个对象中,除非将其销毁的时候或对其赋值的 时候(拷贝和移动的操作很相似)。不过,实践中移动能保证类中的所有状态保持不变,表现 良好。
  • 一个 std::thread 实例可以作为移动源,转移到新(以默认构造方式)的 std::thread 实 例中。还有, std::string 可以通过移动原始数据进行构造,并且保留原始数据的状态,不 过不能保证的是原始数据中该状态是否正确(根据字符串长度或字符数量决定)。

右值引用与函数模板

  • 如果函数模板的参数是声明为 右值 引用,如
template<typename 	T>
void foo(T &&t)
{
}
  • 如果传递左值,模板会自动认为 这个类型 是 左值的引用
  • 如果传递右值,模板会当做普通数据使用。
  • 如:传递右值
//传递数字也是右值
foo(42); // foo(42)
foo(3.14159); // foo<3.14159>
//这个是右值
foo(std::string()); // foo(std::string())
  • 传递左值:T 会被推导为一个左值引用
  • 因为声明为 T &&, 传入左值,认为是引用的引用,所以认为是原始类型(这里是int)的引用(int&)的引用(int &&)
//左值i
int i = 42;
//传递i
foo(i);  // foo(i)
// 认为 foo(int &t) 这意味着函数模板即可以接收左值,又可以接受右值参数。这种方式也是std thread 构造函数 使用的。这样,可调用对象就可以移到内部存储,而非当参数是右值的时候拷贝。

左值引用只能绑定到左值上

左值引用

  • int & i = 42 ; 是错的
  • 因为42是个右值
  • i是个左值
  • 对i(左值)的引用,只能绑定到左值 ,而非右值
  • int const & i = 42 ; 就是ok的,钻了空子
  • 对左值 i 创建一个const 引用,创建了一个临时对象,
  • [cpp]右值和移动_第1张图片

右值 引用

  • && 是c++11 的 右值引用,只能绑定右值,不能绑定左值
int && i = 42//对右值引用i,绑定到右值42上
int j = 42; 	// j是一个左值
int && k =j ;// 错误,左边是右值引用 。	

move的前提:右值通常是临时的

  • 右值 通常都是临时的,所以可以随意修改
  • 如果某个函数参数是一个右值,可以看做为临时存储或者 『窃取』内容
  • 这样的话,不需要拷贝其内容,直接移动其内容
  • 比如动态数组,很大的时候,move内容节省很多内存
  • [cpp]右值和移动_第2张图片
  • 下面的声明,const 的,允许函数能以左值或右值的形式进行传递,但是 都是拷贝的方式来的
void process_copy(std::vector<int> const& vec_) {
std::vector<int> vec(vec_);
vec.push_back(42); }
  • 用右值引用版本的函数来重载这个函数,就能避免在传入右值的时候,函数会进行内部 拷贝的过程

显示移动

  • 将参数值不通过移动,转化为本地变量或者成员变量。
  • std move 之后,x1 就不会再用到了。
  • 所有权到了x2这里。
  • static_cast 强制把x2从左值变为右值
X x1 ;
X x2 = std::move(x1);
X x3 = static_cast<X&&>(x2) ;

void do_stuf(X && x_)
{
  X a(x_); //拷贝
  X b(std::move(x_)); //移动
} 
//这个可以,右值绑定到右值引用上
do_stuff(X());

//
X xx;
do_stuf(xx); //错误,xx是左值。左值不能绑定到右值引用上。

你可能感兴趣的:(c/c++)