Purposes:
- 理解引用折叠
- 理解完美转发
- std::move()与std::forward区别
Premise:
- 左值: 当一个对象被用作左值的时候,用的是对象的身份(在内存中的位置)
- 右值: 当一个对象被用作右值的时候,用的是对象的值(内容), 右值的生命周期很短,表达式结束就会被销毁
- 左值引用: 通常指的是在左值前面加一个’&'符号, 左值引用不能绑定到一个右值上, 这就是我们最常用的引用方式, 例如: int i =42; int &r = i;
- 右值引用:右值引用必须绑定到一个右值上, 我们通常是用’&&'来获得右值引用, 例如: int &&rr = i * 66;
std::move()
template<class T>
typename remove_reference<T>::type&& move (T&& arg) noexcept;
这就是std::move函数的原型, 这里有三个难点:
- remove_reference::type :熟悉模板的童鞋可能知道, 这是模板里的嵌套从属类型, 而这类类型前面是需要加上typename来标注这是一个类型, 而不是一个类
- type后面的两个&& :这个就与引用折叠有关了, 在后面会讲到
- noexcept : std::move是不需要catch exception的, 如果遇到错误, 就结束进程。这与析构函数和delete操作是一样的,这里主要是为了节省编译器消耗。
std::move()函数是没有完美转发的功能的, 不论是左值还是右值, 只要是入了参, 就都是右值。
std::forward()
lvalue (1)
template <class T> T&& forward (typename remove_reference<T>::type& arg) noexcept;
rvalue (2)
template <class T> T&& forward (typename remove_reference<T>::type&& arg) noexcept;
可以看到这里有两个重载的std::forward函数, 再通过引用折叠就可以完成完美转发.
引用折叠
在编译器中, 具体规则是:
1.所有右值引用折叠到右值引用上仍然是一个右值引用。(A&& && 变成 A&&)
2.所有的其他引用类型之间的折叠都将变成左值引用。 (A& & 变成 A&; A& && 变成 A&; A&& & 变成 A&)
也可以说成: 只要含有&就被认为是左值引用, 其余就只剩&& &&了, 这当然就是右值引用。
这就是std::move与std::forward的中心
实际上,开发者是不能写出类似于int& &&i = 0;这种代码的, 这是编译器不允许的,只能通过类型包装或者模板参数间接的使用引用折叠。
Summary:
其实理解引用折叠与完美转发不难。
重点是要掌握基础概念:左值、右值