23

在 C++11 中,值参数(和其他值)在返回时享受隐式移动:

A func(A a) {
    return a; // uses A::A(A&&) if it exists
}

至少在 MSVC 2010 中,右值引用参数需要std::move

A func(A && a) {
    return a; // uses A::A(A const&) even if A::A(A&&) exists
}

我想在内部函数中,右值引用和值的行为相似,唯一的区别是在值的情况下,函数本身负责销毁,而对于右值引用,责任在外部。

在标准中区别对待它们的动机是什么?

4

3 回答 3

28

标准化委员会花费了大量精力来创建措辞,以便仅在两种情况下才会发生动作:

  1. 当这样做显然是安全的。
  2. 当用户明确询问时(通过std::move或类似的演员表)。

毫无疑问,值参数将在函数结束时被销毁。因此,通过 move 将其返回显然是安全的;返回后其他代码无法触及它(除非您故意尝试破坏事物,在这种情况下您可能触发了未定义的行为)。因此,它可以从返回中移动。

&&变量可能指的是临时变量。但它可能指的是一个左值(一个命名变量)。因此,离开它显然是不安全的;原始变量可能潜伏在周围。而且由于您没有明确要求从中移动(即:您没有调用std::move此函数),因此不会发生移动。

唯一一次&&隐式移动变量(即:没有std::move)是在您返回它时。std::move<T>返回一个T&&。该返回值调用移动构造函数是合法的,因为它是一个返回值。

现在很难A func(A &&a)用左值调用而不调用std::move(或等效转换)。所以从技术上讲,隐式移动类型参数应该&&没问题。但是标准委员会希望移动对&&类型来说是明确的,只是为了确保移动不会隐式地发生在这个函数的范围内。也就是说,它不能使用关于&&来自何处的函数外知识。

一般来说,你应该只&&在两种情况下使用参数:或者你正在编写一个移动构造函数(或移动赋值运算符,但即使这也可以通过值来完成),或者你正在编写一个转发函数。&&可能还有其他一些情况,但除非您有特别的想法,否则您不应该选择一种类型。如果A是可移动类型,则按值取值。

于 2012-03-19T23:21:24.500 回答
6

这是由P0527P1825为 C++20 修复的。将函数参数绑定到右值引用的唯一方法是源是临时的,或者调用者将非临时的显式转换为右值(例如,使用std::move)。因此,这种“强制优化”被认为是安全的。

于 2019-10-19T01:04:06.857 回答
5

在您的第一种情况下,编译器知道它a正在消失,并且没有任何东西能够坚持它:显然,这个对象可以被移动,如果不是,它将被销毁。在第二种情况下,右值引用表示允许从对象中移动,并且调用者不希望对象留在周围。但是,是否利用此权限是函数的选择,并且可能存在函数有时想要从参数中移动而有时又不想移动的原因。如果编译器可以自由地离开这个对象,就没有办法阻止编译器这样做。但是,std::move(a)已经有一种方法可以指示希望从对象中移动。

标准中的一般规则是编译器只会隐式移动已知会消失的对象。当一个右值引用进来时,编译器并不真正知道该对象即将离开:如果它被显式std::move()编辑,它实际上仍然存在。

于 2012-03-19T23:24:57.170 回答