转发引用与右值引用的区别¶
带右值引用符号不一定就是右值引用,这种不确定类型的引用称为转发引用:
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)...);
};