为什么rvalue引用与移动语义相关联?

scd*_*dmb 3 c++ c++11

当我阅读一些文章时,通常会一起描述rvalue引用和移动语义.但是据我所知,rvalue引用只是对rvalues的引用,并且与移动语义无关.并且甚至可以在不使用右值引用的情况下实现移动语义.所以问题是,为什么移动构造函数/运算符=使用rvalue引用?是不是只是为了更容易编写代码?

Nic*_*las 8

考虑问题.我们想要支持两种基本的移动操作:移动"构造"并移动"赋值".我在那里使用引用因为我们不一定要用构造函数或移动赋值运算符来实现它们; 我们可以用别的东西.

移动"构造"意味着通过从现有对象传输内容来创建新对象,这样删除旧对象不会释放现在在新对象中使用的资源.移动"赋值"意味着获取预先存在的对象并从现有对象传输内容,这样删除旧对象不会释放现在在新对象中使用的资源.

好的,所以这些是我们想要做的操作.那么,怎么办呢?

采取行动"建设".虽然我们没有一个构造函数调用来实现这一点,我们真的希望.我们不想强迫人们做两阶段的移动建设,即使它背后有一些神奇的功能调用.所以我们希望能够将运动作为构造函数来实现.好的.

这里的问题1:构造函数没有名称.因此,您只能根据参数类型和重载分辨率区分它们.我们知道类型对象的移动构造函数T必须将类型的对象T作为参数.因为它只需要一个参数,所以它看起来就像一个复制构造函数.

好的,现在我们需要一些方法来满足重载.我们可以介绍一些标准库类型,a std::move_ref.它会是std::reference_wrapper,但它将是一个独特的类型.因此,你可以说移动构造函数是一个构造函数,它接受一个std::move_ref<T>.好吧,好的:问题解决了.

只有没有; 我们现在有新问题.考虑以下代码:

std::string MakeAString() { return std::string("foo"); }

std::string data = MakeAString();
Run Code Online (Sandbox Code Playgroud)

忽略elision,C++ 11的表达式值类别规则声明从值返回的类型是一个prvalue.因此,它会自动被移动构造函数/赋值运算符使用尽可能.不需要std::move等等.

要按照你的方式做到这一点需要:

std::string MakeAString() { return std::move(std::string("foo")); }

std::string data = std::move(MakeAString());
Run Code Online (Sandbox Code Playgroud)

std::move为避免复制,需要这两个调用.你必须移出临时值并进入返回值,然后移出返回值并进入data(再次忽略省略).

如果您认为这只是一个小麻烦,请考虑其他左值参考购买我们:完美转发.如果没有特殊的引用 - 折叠规则,您就无法编写正确的转发函数来完美地转发复制和移动语义.std::move_ref将是一个真正的C++类型; 你不能像使用右值引用一样打击任意规则,例如引用折叠到它上面.

在一天结束时,您需要某种语言构造,而不仅仅是库类型.通过使其成为一种新的引用,您可以为可以绑定到该引用的内容(以及不能引用的内容)定义新规则.您可以定义特殊的参考折叠规则,以实现完美的转发.