0%

cpp右值

左值: 可以赋值、取地址的值
右值:字面值、临时值等无法取地址的值
左值引用: 可以指向左值,不能指向右值的引用。const & 左值可以指向右值,

1
const int &ref_a = 5; // 编译正常

右值引用: 可以指向右值,不能指向左值的引用。

右值引用可以修改右值

右值引用可以指向左值 – std::move

1
2
int a = 10;
int &&ref_a_left = std::move(a);

std::move 只是强制把左值转换成右值,让右值引用可以指向左值,相当于强制类型转换;

  1. 从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝。
  2. 右值引用可以直接指向右值,也可以通过std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。
  3. 作为函数形参时,右值引用更灵活。虽然const左值引用也可以做到左右值都接受,但它无法修改,有一定局限性。

应用场景,实现容器的移动。增加了移动构造函数移动赋值重载函数

std::move 解析

1
2
3
4
5
6
7
8
9
10
11
  /**
* @brief Convert a value to an rvalue.
* @param __t A thing of arbitrary type.
* @return The parameter cast to an rvalue-reference to allow moving it.
*/
template <typename _Tp>
constexpr typename std::remove_reference<_Tp>::type &&
move(_Tp &&__t) noexcept
{
return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
}

std::move是个模板函数,constexpr 表示这个函数是编译期常量,在编译期间求值,不会增加运行时开销;typename 常在模板函数中使用,
告诉编译期,后边跟的std::remove_reference<_Tp>::type 是个类型。

std::remove_reference有3个定义, 作用是移除引用,返回值本身:

1
2
3
4
5
6
7
8
9
10
11
12
/// remove_reference
template<typename _Tp>
struct remove_reference
{ typedef _Tp type; };

template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp type; };

template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp type; };

因此return static_cast<typename std::remove_reference<_Tp>::type &&>(__t); 就是强制转换成右值引用返回。

完美转发, std::forward

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* @brief Forward an lvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template <typename _Tp>
constexpr _Tp &&
forward(typename std::remove_reference<_Tp>::type &__t) noexcept
{
return static_cast<_Tp &&>(__t);
}

/**
* @brief Forward an rvalue.
* @return The parameter cast to the specified type.
*
* This function is used to implement "perfect forwarding".
*/
template <typename _Tp>
constexpr _Tp &&
forward(typename std::remove_reference<_Tp>::type &&__t) noexcept
{
static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
" substituting _Tp is an lvalue reference type");
return static_cast<_Tp &&>(__t);
}

这里用到了引用折叠,也就是多个引用类型叠加时,最后的类型是什么?引用有左值引用、右值引用,叠加后有4种状态:

  1. 左引 - 左引: T& & – 左引
  2. 左引 - 右引: T& && – 左引
  3. 右引 - 左引: T&& & – 左引
  4. 右引 - 右引: T&& && – 右引

折叠的规则是: 有1个左引,最后就是左引。

完美转发经典场景:

1
2
3
4
template <class... Args>
void forward(Args&&... args) {
f(std::forward<Args>(args)...);
}
  • std::move 强制转换成右值
  • std::forward 是什么值,就转成什么值,中间过渡传递的。它会将输入的参数原封不动地传递到下一个函数中,这个“原封不动”指的是,如果输入的参数是左值,那么传递给下一个函数的参数的也是左值;如果输入的参数是右值,那么传递给下一个函数的参数的也是右值。