转发引用与右值引用的区别

带右值引用符号不一定就是右值引用,这种不确定类型的引用称为转发引用:

template<typename T>
void f(T&&) {} //@ T&&不一定是右值引用

int a = 1;
f(a); //@ T推断为int&,T&&是int& &&,折叠为int&,是左值引用
f(1); //@ T推断为int,T&&是int&&,右值引用
auto&& b = a; //@ int& b = a,左值引用
auto&& c = 1; //@ int&& c = 1,右值引用

转发引用必须严格按 T&& 的形式涉及类型推断:

template<typename T>
void f(std::vector<T>&&) {} //@ 右值引用而非转发引用

std::vector<int> v;
f(v); //@ 错误

template<typename T>
void g(const T&&) {} //@ 右值引用而非转发引用

int i = 1;
g(i); //@ 错误

T&& 在模板中也可能不涉及类型推断:

template<class T, class Allocator = allocator<T>>
class vector {
public:
    void push_back(T&& x); //@ 右值引用

    template <class... Args>
    void emplace_back(Args&&... args); //@ 转发引用
    ...
};

std::vector<A> v; //@ 实例化指定了T

//@ 对应的实例化为
class vector<A, allocator<A>> {
public:
    void push_back(A&& x); //@ 不涉及类型推断,右值引用

    template <class... Args>
    void emplace_back(Args&&... args); //@ 转发引用
    //...
};

auto&& 都是转发引用,因为一定涉及类型推断。完美转发中,如果想在转发前修改要转发的值,可以用 auto&& 存储结果,修改后再转发。

template<typename T>
void f(T x)
{
    auto&& res = doSomething(x);
    doSomethingElse(res);
    set(std::forward<decltype(res)>(res));
}

lambda 中也可以使用完美转发:

auto f = [](auto&& x) { return g(std::forward<decltype(x)>(x)); };

//@ 转发任意数量实参
auto f = [](auto&&... args) {
	return g(std::forward<decltype(args)>(args)...);
};