对右值引用使用std::move,对转发引用使用std::forward

右值引用只会绑定到可移动对象上,因此应该使用 std::move。转发引用用右值初始化时才是右值引用,因此应当使用 std::forward

class A {
public:
    A(A&& rhs) : s(std::move(rhs.s)), p(std::move(rhs.p)) {}

    template<typename T>
    void f(T&& x)
    {
        s = std::forward<T>(x);
    }
private:
    std::string s;
    std::shared_ptr<int> p;
};

如果希望只有在移动构造函数保证不抛异常时才能转为右值,则可以用 std::move_if_noexcept 替代 std::move

class A {
public:
	A() = default;
	A(const A&) { std::cout << 1 << "\n"; }
	A(A&&) { std::cout << 2 << "\n"; }
};

class B {
public:
	B() {}
	B(const B&) noexcept { std::cout << 3 << "\n"; }
	B(B&&) noexcept { std::cout << 4 << "\n"; }
};

int main()
{
	A a;
	A a2 = std::move_if_noexcept(a); //@ 1
	B b;
	B b2 = std::move_if_noexcept(b); //@ 4
}

如果返回对象传入时是右值引用或转发引用,在返回时要用 std::movestd::forward 转换。返回类型不需要声明为引用,按值传递即可。

A f(A&& a)
{
    doSomething(a);
    return std::move(a);
}


template<typename T>
A g(T&& x)
{
    doSomething(x);
    return std::forward<T>(x);
}

返回局部变量时,不需要使用 std::move 来优化。

A makeA()
{
    A a;
    return std::move(a); //@ 画蛇添足
}

局部变量会直接创建在为返回值分配的内存上,从而避免拷贝,这是 C++ 标准诞生时就有的 RVO(return value optimization)。RVO 的要求十分严谨,它要求局部对象类型与返回值类型相同,且返回的就是局部对象本身,而使用了std::move 反而不满足 RVO 的要求。此外 RVO 只是种优化,编译器可以选择不采用,但标准规定,即使编译器不省略拷贝,返回对象也会被作为右值处理,所以 std::move 是多余的。

A makeA()
{
    return A{};
}

auto x = makeA(); //@ 只需要调用一次A的默认构造函数