《Effective Modern C++》条款24:区分万能引用和右值引用
简述
万能引用和右值引用都用”T&&“表示。
万能引用
所谓万能引用,即它既可以绑定左值,又可以绑定右值。一般用于表示型别推导的结果。
右值引用
右值引用,顾名思义就是只能绑定到右值,它的主要作用是作为可移动对象的标识。
详解
万能引用
万能引用有两种最常见的场景,这两种场景都涉及型别推导,它们分别是:
模板函数的形参
1
2
3
4
5
6
7
8template<typename T>
void f(T&& param); // param是个万能引用
Widget w;
f(w); // param的型别是左值引用,即Widget&
f(std::move(w)); // param的型别是右值引用,即Widget&&用于auto声明
1
2
3
4
5Widget w;
auto&& w1 = w; // w1是个左值引用,即Widget&
auto&& w2 = std::move(w); // w2是个右值引用,即Widget&&
万能引用首先是一个引用,所以初始化是必须的,且初始化的对象决定了它代表的是左值还是右值引用。
几个要点:
万能引用的声明形式必须是”T&&“,且型别推导必须是涉及param本身:
模板函数的形参并不一定涉及型别推导
1
2
3
4
5
6
7template<typename T>
void f(std::vector<T>&& param); // param是个右值引用,
// 因为型别已确定是std::vector<T>&&,
// 而非T&&
std::vector<int> v;
f(v); // 编译错误!不能给一个右值引用绑定一个左值
f(std::move(v)); // 编译通过位于模板内并不能保证涉及型别推导
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// vector的类声明
template<class T, class Allocator = allocator<T>>
class vector {
public:
...
void push_back(T&& param); // param是个右值引用,虽然形参的声明形式为”T&&“,
// 但是调用该函数并不涉及型别推导,
// 因为T的类型在创建vector时已确定。
template<class... Args>
void emplace_back(Args... args); // args是个万能引用
...
};
std::vector<Widget> v; // 此时push_back的形参型别已确定为Widget,
// 但是emplace_back的形参需要推导。
使用const修饰的”T&&”一定是右值引用
1
2template<typename T>
void f(const T&& param); // param是个右值引用