1. lvalue / rvalue
An lvalue is an expression that refers to a memory location and allows us to take the address of that memory location via the & operator. An rvalue is an expression that is not an lvalue.
即,存在于内存且可用&
取地址的表达式为右值,否则为左值。在Value categories 详细的解释了如何区分左值和右值。
1.1 lvalue
int i = 42;
i = 43; // ok, i is an lvalue
int* p = &i; // ok, i is an lvalue
int& foo();
foo() = 42; // ok, foo() is an lvalue
int* p1 = &foo(); // ok, foo() is an lvalue
1.2 rvalue
int foobar();
int j = 0;
j = foobar(); // ok, foobar() is an rvalue
int* p2 = &foobar(); // error, cannot take the address of an rvalue
j = 42; // ok, 42 is an rvalue
2. lvalue reference / rvalue reference
lvalue reference: T&
rvalue reference: T&&
3. Rvalue reference
右值引用的两大意义
- move semantics
- perfect forwarding
4. Universal references VS Rvalue references
4.1 形式
同为 T&&
4.2 Universal references
可左值可右值,和右值引用的形式相同,一般在以下两种情况出现
- 函数模板:
template <typename T> void f(T&&);
- auto 声明:
auto&& var = var1
4.2.1 reference collapsing
通用引用的类型推导规格,即为reference collapsing,如下
- A& & becomes A&
- A& && becomes A&
- A&& & becomes A&
- A&& && becomes A&&
4.3 Rvalue references
只能绑定到右值。除去Universal references就是右值引用了。
5. move semantics
移动语义,主要解决对象资源所有权转移的问题。在C++11之前,由于移动语义的缺失造成的资源转移需要通过构造(复制)/析构来实现是影响C++性能的一个重要原因。
5.1 std::move
std::move
无条件将类型转换为右值。其作用是为了强制实现移动语义。
// vs 2017 : move
template<class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) _NOEXCEPT
{ // forward _Arg as movable
return (static_cast<remove_reference_t<_Ty>&&>(_Arg));
}
例如强制将左值转换为右值
int a = 32; // lvalue
f(std::move(a)); // rvalue
5.2 perfect forwarding
问题引入,如何将一个函数的参数原封不动的传给另外一个函数?std::forward
根据类型转换为左值或右值,从而实现完美转发。
// vs 2017 : forward
template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) _NOEXCEPT
{ // forward an lvalue as either an lvalue or an rvalue
return (static_cast<_Ty&&>(_Arg));
}
template<class _Ty>
_NODISCARD constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) _NOEXCEPT
{ // forward an rvalue as an rvalue
static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}
通常用在需要将绑定右值的左值转换为右值的地方,比如,函数参数总为左值,但是其可能绑定了一个右值引用。
Named variables and parameters of rvalue reference type are lvalues.
例如:
int&& a = 32;
void func(int&& param) // 函数参数总为左值
{}
上边a
和param
都是左值,但是他们都是绑定到了右值上。
如果有两个重载函数
void f1(const int&);
void f1(int&&);
当通过f1(a)
调用时,都会调用void f1(const int&);
我们可以通过使用std::forward
来转换
fw(std::forward<decltype(a)>(a));
其更常用在Universal references的函数中来调用其他函数,因为参数都是左值,所以需要用 std::forward
将其类型转换为左值或右值;
template <typename T>
void f(T&& param)
{
f1(param); // always: void f1(const int&)
}
template <typename T>
void f(T&& param)
{
f1(std::forward<T>(param)); // depend on T
}