右值、移动语义和完美转发

本文最后更新于:November 29, 2024 pm

本系列博客将会带读者深入了解C++11中耐人寻味的一系列特性——右值引用、移动语义和完美转发。右值引用作为纽带,将移动语义和完美转发紧密联系。探索这三个兄弟,会给人一种先把书读厚,再把书读薄的感觉

右值引用、移动语义和完美转发

当你第一次听说时,移动语义和完美转发十分顾名思义:

  • 移动语义让编译器用开销更小的移动操作,代替昂贵的拷贝操作。

    如同拷贝构造函数和拷贝复制运算,移动构造函数和移动赋值运算让你控制在移动语义上的行为。移动语义使得”move-only”类型成为可能,比如std::unique_ptrstd::futurestd::thread

  • 完美转发

    完美转发使得我们可以写一个函数模板,它能够接受任意的参数并且将所有参数转发给其它函数,同时使得此函数收到的参数和我们传给完美转发函数模板的参数一模一样——针对表达式的类型和值的类型而言。

右值作为胶水,将移动语义和完美转发紧紧地捆绑在一起。右值是支持这两种操作底层的语言机制。

随着你使用这些新特性的经验增多,你越会发现你的第一印象仅仅是冰山一角,右值、移动语义和完美转发构建的世界比看上去的复杂得多。

std::move,顾名思义,移动。本质上,它却并不移动任何东西,它就是一个静态强制类型转换static_cast;另一方面,完美转发,也不是完美的。移动操作也不总是比拷贝操作导致更小的开销;即使有时移动操作优于拷贝操作,它的效率和性能也没有你想象中的那么好;并且,在可以移动的情况下,我们也不总是调用std::move; “type&&”的结构,也不总是代表了一个右值引用。

不论你对这些特征深挖多少,它们总是看起来还有更多待挖掘的内容。幸运的是,它们的深度也是有限的,这一章将会带你理解它们的基本原理。当你理解了之后,C++11的这一块内容将会更有价值。你将会了解到std::movestd::forward的使用习惯;你也会对有着模糊本质的”type&&”习以为常;你更会理解移动操作也有表现变化多端的一面。

所有的这些碎片汇集到一起,你将会感觉回到了原地,因为右值、移动语义和完美转发又会再一次看起来非常直白和简单——只不过这一次,它们不再会进一步困扰你。

我们知道,如果一个表达式可以被取地址,那么它常常是个左值,反之为右值。因此,在接下来的条款中,需要谨记:

函数的形参总是左值,即:

1
void f(Widget&& w);

在函数f中,w是个左值,也是个Widget类型的右值引用。

系列导航

Item23:理解std::move和std::forward

Item24:区分万能引用和右值引用

Item25:在右值引用上使用std::move,在万能引用上使用std::forward

Item26:避免在万能引用上重载

Item27:熟悉用于替代在万能引用上重载的方法

Item28:理解引用折叠

Item29:假设移动操作不存在、代价高且不宜使用

Item30:熟悉完美转发失败的情形

内容出自对于 Effective Modern C++[1] Item23~30的学习笔记,并依照原文顺序组织


  1. Effective Modern C++ by Scott Meyers(O’Reilly). Copyright 2015 Scott Meyers, 978-1-491-90399-5

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!