C++ 为什么C++;11是否对值参数进行隐式移动,但对右值参数不进行隐式移动?

C++ 为什么C++;11是否对值参数进行隐式移动,但对右值参数不进行隐式移动?,c++,c++11,move-semantics,rvalue-reference,c++20,C++,C++11,Move Semantics,Rvalue Reference,C++20,在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 } 我可以想象,在函数内部,右值引

在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
}
我可以想象,在函数内部,右值引用和值的行为相似,唯一的区别是,对于值,函数本身负责破坏,而对于右值引用,责任在外部


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

标准化委员会花费了巨大的努力来创造措辞,以便只有在两种情况下才会采取行动:

  • 当这样做显然是安全的时候
  • 当用户明确要求时(通过
    std::move
    或类似的强制转换)
  • 值参数无疑将在函数末尾被销毁。因此,移动归还显然是安全的;它在返回后不能被其他代码触及(除非您故意试图破坏东西,在这种情况下,您可能触发了未定义的行为)。因此,它可以从返回中移动

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

    &&
    变量将从隐式移动的唯一时间(即:不带
    std::move
    )是在您返回它时
    std::move
    返回一个
    T&
    。该返回值调用move构造函数是合法的,因为它是一个返回值

    现在,如果不调用
    std::move
    (或等效的强制转换),就很难用左值调用
    func(A&&A)
    。因此,从技术上讲,
    &&
    类型的参数可以隐式地从中移动。但是标准委员会希望移动对于
    &
    类型是明确的,只是为了确保移动不会在这个函数的范围内隐式发生。也就是说,它不能使用关于
    &&
    来源的函数外知识


    通常,在两种情况下,只能通过
    &&
    获取参数:要么编写移动构造函数(或移动赋值运算符,但即使是通过值也可以),要么编写转发函数。可能还有其他一些情况,但除非你有特别的想法,否则你不应该把
    &&
    当作一种类型。如果
    A
    是一个可移动类型,那么只需按值获取它。

    在第一种情况下,编译器知道
    A
    正在消失,并且没有任何东西能够附着在它上面:很明显,这个对象可以移动,如果不是,它将被销毁。在第二种情况下,rvalue引用表示允许从对象移动,并且调用者不希望对象停留在附近。但是,函数是否利用此权限是函数的选择,函数有时希望离开参数,有时不想离开,这可能是有原因的。如果编译器有权离开这个对象,那么就没有办法阻止编译器这样做。但是,使用
    std::move(a)
    已经有一种方法可以指示需要从对象移动


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

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

    如果你有一个函数
    af()
    ,如果您调用
    a(f())
    会调用
    a(a&&)
    吗(因为您是用临时变量调用它的)?我也从来没有真正弄清楚这方面的规则。@Seth,简言之,是的。考虑<代码> f~(/>)>代码>,它按值返回<代码> A<代码>的实例。通过这样做,它既不能保证自身也不能保证它调用的函数能够持有对该实例的引用。现在,
    a(f())
    立即将该实例传递给
    a()
    ,而不给调用者任何机会持有对其本身的引用(例如,如果您发布了
    a my_a;a(my_a=f());
    ,情况将大不相同)。但在您的情况下,调用
    a(a&&)
    是完全安全的,而且它实际上比
    a(const a&)
    IIRC具有优先级。我认为您的答案有点含糊不清。我希望其中一个能给出一个清晰的语句:(1)
    &&
    参数必须隐式移动,或者(2)不能隐式移动,或者(3)编译器可以任意选择。我认为,在这种情况下,隐式移动是强制性的,MSVC版本是不正确的<代码>返回x必须隐式重写为
    返回移动(x)
    ,其中
    x
    是一个参数,
    x
    a&
    ,(并且
    a
    本身不是引用类型!),返回类型是
    a
    (甚至
    a&
    ?)。我认为这保证是安全的。(我的第二个评论,让我做一个不同的观察)我不认为这有意义:“……但它可能是指左值(一个命名变量)……”一个
    &&
    参数的全部要点是编译器说“我不在乎你以任何方式做什么。”